<?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: Nick Campbell</title>
    <description>The latest articles on DEV Community by Nick Campbell (@greysquirr3l).</description>
    <link>https://dev.to/greysquirr3l</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2461717%2F948e00ce-525b-4990-b13c-28eb39dcec3a.jpeg</url>
      <title>DEV Community: Nick Campbell</title>
      <link>https://dev.to/greysquirr3l</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/greysquirr3l"/>
    <language>en</language>
    <item>
      <title>I built a linter that removes AI fingerprints from code</title>
      <dc:creator>Nick Campbell</dc:creator>
      <pubDate>Fri, 10 Apr 2026 18:22:20 +0000</pubDate>
      <link>https://dev.to/greysquirr3l/i-built-a-linter-that-removes-ai-fingerprints-from-code-1dmd</link>
      <guid>https://dev.to/greysquirr3l/i-built-a-linter-that-removes-ai-fingerprints-from-code-1dmd</guid>
      <description>&lt;p&gt;Some communities have decided that how code was written matters more than whether it works.&lt;/p&gt;

&lt;p&gt;Not all communities. dev.to isn't one of them, and honestly that's part of why I'm posting here. But if you've spent time on certain forums, subreddits, or open source projects, you've seen the pattern: a review that ignores the actual code and goes straight to "this looks AI-generated." A thread about contribution quality that's really about provenance. A project that rejects a working patch because someone ran it through a detector.&lt;/p&gt;

&lt;p&gt;The failure mode isn't "a robot touched it." It's "the human didn't understand or verify what they shipped." Those aren't the same thing, and conflating them has made some corners of the dev community genuinely hostile to LLM-assisted work regardless of quality.&lt;/p&gt;

&lt;p&gt;So I built a tool.&lt;/p&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/greysquirr3l" rel="noopener noreferrer"&gt;
        greysquirr3l
      &lt;/a&gt; / &lt;a href="https://github.com/greysquirr3l/papertowel" rel="noopener noreferrer"&gt;
        papertowel
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      AI code fingerprint scrubber and git history humanizer
    &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;papertowel&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/greysquirr3l/papertowel/assets/img/papertowel_logo.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%2Fgreysquirr3l%2Fpapertowel%2FHEAD%2Fassets%2Fimg%2Fpapertowel_logo.png" alt="Papertowl"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Cleaning up the slop.&lt;/p&gt;

&lt;p&gt;You know how some communities have decided that &lt;em&gt;how&lt;/em&gt; code was written matters more than &lt;em&gt;whether it's any good&lt;/em&gt;? How the same people who happily accept copy-pasted Stack Overflow answers and half-remembered blog post patterns suddenly care deeply about provenance when an LLM enters the loop?&lt;/p&gt;
&lt;p&gt;Yeah. This tool exists because of them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;papertowel&lt;/strong&gt; detects and removes the stylistic fingerprints that scream "an AI wrote this." It's a specialized linter for the tells that have become forensic evidence in spaces where AI-assisted development is treated as original sin rather than tool use.&lt;/p&gt;
&lt;p&gt;The irony of using AI to build a tool that hides AI involvement is not lost on me. I'm leaning into it.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What it does&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;The Scrubber&lt;/strong&gt; — static analysis of your codebase to find and fix the obvious tells:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Slop vocabulary ("robust," "comprehensive," "streamlined," "utilize," "leverage")&lt;/li&gt;
&lt;li&gt;Over-documentation (comments that restate what…&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/greysquirr3l/papertowel" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/greysquirr3l/papertowel" rel="noopener noreferrer"&gt;papertowel&lt;/a&gt; is a scrubber for AI stylistic fingerprints. It detects and removes the tells that have become forensic evidence in spaces where AI assistance is treated as original sin.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it actually does
&lt;/h2&gt;

&lt;p&gt;The scrubber catches patterns that cluster in LLM output:&lt;/p&gt;

&lt;p&gt;Slop vocabulary — words like "robust," "comprehensive," "streamlined," "utilize," "leverage." Individually harmless. In concentration, they read like a product spec.&lt;/p&gt;

&lt;p&gt;Over-documentation — comments that restate what code obviously does. &lt;code&gt;// Helper function to calculate the sum of two integers&lt;/code&gt; above a function named &lt;code&gt;add&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Cookie-cutter README structure — you know the one. Emoji section headers. Checkmark feature lists. Getting Started → Prerequisites → Installation → Usage → Contributing → License. Every project generated in the same 24 hours has the same skeleton.&lt;/p&gt;

&lt;p&gt;Metadata artifacts — CONTRIBUTING.md, CODE_OF_CONDUCT.md, SECURITY.md, and perfect GitHub issue templates all in commit one.&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="c"&gt;# See what you're working with&lt;/span&gt;
papertowel scan &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Fix it&lt;/span&gt;
papertowel scrub &lt;span class="nb"&gt;.&lt;/span&gt;
papertowel scrub &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--dry-run&lt;/span&gt;  &lt;span class="c"&gt;# if you want to look first&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's also a wringer for git history humanization, but that's a separate post.&lt;/p&gt;

&lt;h2&gt;
  
  
  The recipe system
&lt;/h2&gt;

&lt;p&gt;The scrubber is pattern-driven. Detection rules are TOML files — regex patterns with optional replacements, scoped to file types. No Rust required to extend it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[[rules]]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"slop-utilize"&lt;/span&gt;
&lt;span class="py"&gt;pattern&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'\butilize\b'&lt;/span&gt;
&lt;span class="py"&gt;replacement&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"use"&lt;/span&gt;
&lt;span class="py"&gt;applies_to&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"*.rs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"*.go"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"*.ts"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;severity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"medium"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The built-in recipes are in &lt;code&gt;src/recipes&lt;/code&gt;. If you've noticed a fingerprint papertowel misses, a recipe PR is the fastest path from "this annoys me" to "this is fixed." That's the main thing I'm looking for right now — more recipes, more patterns, better coverage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Yes, I used AI to build it&lt;/strong&gt; ...quickly.&lt;br&gt;
The irony is not subtle. I'm leaning into it.&lt;/p&gt;

&lt;p&gt;A tool that detects AI fingerprints, built with AI assistance, to help people ship AI-assisted code that doesn't get flagged. The recursive self-reference is the point.&lt;/p&gt;

&lt;p&gt;Code quality is about quality. Ship working code, understand what it does, verify it solves the problem. Who wrote the first draft is incidental.&lt;/p&gt;




&lt;p&gt;&lt;code&gt;cargo install papertowel&lt;/code&gt; if you want to try it. &lt;/p&gt;

&lt;h2&gt;
  
  
  Recipe contributions welcome.
&lt;/h2&gt;

</description>
      <category>rust</category>
      <category>opensource</category>
      <category>tooling</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Depresso-Tron 418: I Built a Bureaucratic Coffee Machine That Cannot Make Coffee</title>
      <dc:creator>Nick Campbell</dc:creator>
      <pubDate>Fri, 03 Apr 2026 14:27:09 +0000</pubDate>
      <link>https://dev.to/greysquirr3l/depresso-tron-418-i-built-a-bureaucratic-coffee-machine-that-cannot-make-coffee-33pl</link>
      <guid>https://dev.to/greysquirr3l/depresso-tron-418-i-built-a-bureaucratic-coffee-machine-that-cannot-make-coffee-33pl</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/aprilfools-2026"&gt;DEV April Fools Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I want to be clear about something upfront: this server has been running in production for three days and has successfully brewed zero cups of coffee. I consider this a success.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://github.com/greysquirr3l/depresso-tron-418" rel="noopener noreferrer"&gt;Depresso-Tron 418&lt;/a&gt; is an RFC 2324-compliant HTCPCP server. It implements the BREW and WHEN methods from the Hyper Text Coffee Pot Control Protocol, which is a real protocol that Larry Masinter published on April 1, 1998, and which the Internet Engineering Task Force technically never rescinded.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;You can try it right now at &lt;a href="https://coffee.smartservices.tech" rel="noopener noreferrer"&gt;coffee.smartservices.tech&lt;/a&gt;. I recommend going in without reading further. The experience is better cold.&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/greysquirr3l" rel="noopener noreferrer"&gt;
        greysquirr3l
      &lt;/a&gt; / &lt;a href="https://github.com/greysquirr3l/depresso-tron-418" rel="noopener noreferrer"&gt;
        depresso-tron-418
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      An RFC 2324-compliant HTCPCP server that will earnestly refuse to make you coffee.
    &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;Depresso-Tron 418&lt;/h1&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;An RFC 2324-compliant HTCPCP server that will earnestly refuse to make you coffee.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/greysquirr3l/depresso-tron-418/assets/img/cover-image.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%2Fgreysquirr3l%2Fdepresso-tron-418%2FHEAD%2Fassets%2Fimg%2Fcover-image.png" alt="Depresso-Tron Logo"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;April Fools Challenge entry&lt;/strong&gt; · &lt;a href="https://dev.to/t/418challenge" rel="nofollow"&gt;#418challenge&lt;/a&gt; · &lt;a href="https://dev.to/t/devchallenge" rel="nofollow"&gt;#devchallenge&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://coffee.smartservices.tech" rel="nofollow noopener noreferrer"&gt;coffee.smartservices.tech&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;The Anti-Value Proposition&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Most applications help you manage your time. Depresso-Tron 418 actively wastes it by forcing you to navigate a multi-stage, bureaucratically-correct approval pipeline in order to "brew" a digital cup of coffee that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;cannot be consumed&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;does not exist as a file&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;will almost certainly be refused&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Every engineer knows that the true value of a system is proportional to how many hoops it makes you jump through. By that metric, this is the most valuable software ever written.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features (All Load-Bearing)&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;RFC 2324 Compliance&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;The server implements the &lt;code&gt;BREW&lt;/code&gt; and &lt;code&gt;WHEN&lt;/code&gt; methods per the Hyper Text Coffee Pot Control Protocol. &lt;code&gt;WHEN&lt;/code&gt; controls the milk pour once the digital brew has been started. Using &lt;code&gt;http://&lt;/code&gt; instead of &lt;code&gt;coffee://&lt;/code&gt; does not trigger a…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/greysquirr3l/depresso-tron-418" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


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

&lt;p&gt;Go 1.25, net/http, HTMX 2.0.4 with SSE extension, SQLite via &lt;a href="https://modernc.org/sqlite" rel="noopener noreferrer"&gt;modernc.org/sqlite&lt;/a&gt; (pure Go, no CGO), Gemini API via &lt;a href="https://github.com/google/generative-ai-go" rel="noopener noreferrer"&gt;github.com/google/generative-ai-go&lt;/a&gt;. Everything — templates, static files, the teapot image — is compiled into the binary via //go:embed. The Docker image is 27MB on a scratch base.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Category
&lt;/h2&gt;

&lt;p&gt;Best Google AI Usage, Best Ode to Larry Masinter, or Community Favorite&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Details
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The "Feature" List
&lt;/h3&gt;

&lt;p&gt;Getting coffee out of this server requires completing a multi-stage approval pipeline:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Submit a Brew Permit&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The server takes your application, thinks about it, and may: approve it, reject it, or enter an existential Identity Crisis and refuse to process anything until it figures out what it even is. There's a 15% chance at any time that it spontaneously enters Teapot Mode, where all BREW requests return 418 until it recovers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Pass the Gemini Bean Check&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You describe your coffee beans to the AI barista. The barista is powered by gemini-2.5-flash and is constitutionally incapable of approving anything described as "Folgers," "instant," "K-cup," "store-bought," or "decaf." Describing your beans as decaf triggers an immediate categorical refusal before the API call is even made. The server doesn't negotiate on decaf.&lt;/p&gt;

&lt;p&gt;Approval requires at least 3 of: ethically-sourced, altitude, anaerobic fermentation, micro-lot, terroir, bloom, processing station, varietal, natural process, washed process. If you are rejected, the barista's hostility escalates with each attempt. By rejection #5 it is contractually required to compose its response entirely in iambic pentameter, minimum five lines.&lt;/p&gt;

&lt;p&gt;Gemini API keys are per-session — each visitor supplies their own. The server used to have a global key, and then I thought about what it means to run a publicly accessible endpoint that POSTs arbitrary text to my Gemini quota, and I moved it to per-session faster than you can say "abuse vector."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Solve CaffeineChain™&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before the barista will accept a bean description, your browser must demonstrate commitment by finding a nonce &lt;code&gt;N&lt;/code&gt; such that &lt;code&gt;hex(sha256(seed + N))&lt;/code&gt; starts with cafe. This takes approximately 5–30 seconds of CPU. The computation produces nothing useful. The name does not imply blockchain technology or financial instruments. It implies bureaucracy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Wait for the WHEN Window&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Even if you pass everything above, BREW only triggers within a valid time window. Miss it and the pipeline resets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Watch the SSE stream&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A Server-Sent Events feed narrates your brewing attempt in real time, in the voice of whatever mood the server is currently in. There are five moods. One of them is &lt;em&gt;"IDENTITY CRISIS."&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The External Factors
&lt;/h3&gt;

&lt;p&gt;I also connected it to the real world in ways that can only make things worse:&lt;/p&gt;

&lt;p&gt;If the temperature in Holt, Michigan is 70°F or above, the digital milk has spoiled and you get a &lt;code&gt;503&lt;/code&gt;. (Holt, MI is where the HTCPCP RFC was written. Weather courtesy of Open-Meteo.)&lt;/p&gt;

&lt;p&gt;If Mercury is in retrograde, BREW returns 503 with a sympathetic but firm message about celestial interference. Retrograde periods are hard-coded per RFC 9999 §Circadian, which I self-ratified.&lt;br&gt;
Between &lt;strong&gt;23:00&lt;/strong&gt; and &lt;strong&gt;03:00&lt;/strong&gt; local time, only cold brew is available. Cold brew takes 12 hours. You will not be getting coffee tonight.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Google AI Part
&lt;/h3&gt;

&lt;p&gt;The Gemini barista is the core of the whole thing. Without it, the server is just a bureaucracy simulator. With it, it's a bureaucracy simulator that personally insults your taste in coffee using a language model trained on more text than any human has ever read.&lt;/p&gt;

&lt;p&gt;The escalating hostility system was genuinely fun to build — the system prompt changes based on rejection count, and by the fifth rejection it has to switch to iambic pentameter. What I didn't expect was how good gemini-2.5-flash is at being condescending. It takes to it naturally.&lt;/p&gt;

&lt;p&gt;Getting each user's own API key from Google AI Studio (free tier, no billing required) was the right call both technically and thematically. The server has no secrets. It just has opinions.&lt;/p&gt;

&lt;h2&gt;
  
  
  An Ode to Larry Masinter
&lt;/h2&gt;

&lt;p&gt;RFC 2324 is 28 years old. The 418 I'm a Teapot status code survived every attempt to remove it from the HTTP spec because the HTTP working group ultimately decided it was harmless and the internet had already adopted it emotionally.&lt;/p&gt;

&lt;p&gt;Larry Masinter wrote a protocol for controlling coffee pots over HTTP, complete with BREW, WHEN, add-in headers for milk and sugar, and a coffee-pot-command content type. He put it in the IETF document archive on April 1, 1998. He was the Area Director for Applications at the time. He did it anyway.&lt;/p&gt;

&lt;p&gt;This project is the correct response to that document.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/greysquirr3l/depresso-tron-418" rel="noopener noreferrer"&gt;github.com/greysquirr3l/depresso-tron-418&lt;/a&gt;&lt;br&gt;
Live: &lt;a href="https://coffee.smartservices.tech" rel="noopener noreferrer"&gt;coffee.smartservices.tech&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>418challenge</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
