<?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: Sam</title>
    <description>The latest articles on DEV Community by Sam (@samm_nofilterreview).</description>
    <link>https://dev.to/samm_nofilterreview</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%2F4046580%2F2a2605fc-c5dd-45cb-8da4-bb580273f55b.png</url>
      <title>DEV Community: Sam</title>
      <link>https://dev.to/samm_nofilterreview</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/samm_nofilterreview"/>
    <language>en</language>
    <item>
      <title>How I'm Building Reproducible Agentic Tests for AI Girlfriend Apps</title>
      <dc:creator>Sam</dc:creator>
      <pubDate>Sat, 15 Aug 2026 07:15:23 +0000</pubDate>
      <link>https://dev.to/samm_nofilterreview/how-im-building-reproducible-agentic-tests-for-ai-girlfriend-apps-4a06</link>
      <guid>https://dev.to/samm_nofilterreview/how-im-building-reproducible-agentic-tests-for-ai-girlfriend-apps-4a06</guid>
      <description>&lt;p&gt;AI companion and AI girlfriend apps are a useful stress test for a problem that affects many stateful AI products: the claims users care about are easy to market and surprisingly hard to verify.&lt;/p&gt;

&lt;p&gt;"Long-term memory" can mean remembering a name ten messages later, retaining a fact across sessions, or keeping dozens of facts straight after the conversation has accumulated noise. "Consistent characters" can mean tone, biography, appearance, or all three. A single successful image says almost nothing about the next ten generations.&lt;/p&gt;

&lt;p&gt;Yet many review pages turn those claims into precise-looking scores without showing the paid plan, test date, prompt sequence, transcript, retries, or failed generations behind the number.&lt;/p&gt;

&lt;p&gt;I wanted a test another person could actually rerun.&lt;/p&gt;

&lt;h2&gt;
  
  
  The manual baseline
&lt;/h2&gt;

&lt;p&gt;I started NoFilterReview by buying the products myself. Five paid hands-on tests are live today.&lt;/p&gt;

&lt;p&gt;For each product I record:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the plan and price on the test date&lt;/li&gt;
&lt;li&gt;what the free tier actually allows&lt;/li&gt;
&lt;li&gt;cancellation steps&lt;/li&gt;
&lt;li&gt;privacy and account controls&lt;/li&gt;
&lt;li&gt;refusal and moderation behavior&lt;/li&gt;
&lt;li&gt;dated screenshots and transcripts&lt;/li&gt;
&lt;li&gt;media cost and the failed generations, not just the best output&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That already catches a lot of bad review data. Prices change. Models get replaced. A feature shown on a landing page may not exist in the tested plan. A reviewer can easily mistake one lucky generation for normal quality.&lt;/p&gt;

&lt;p&gt;But manual testing alone has a serious weakness: I am part of the instrument. My phrasing, timing, patience, and expectations can change the result.&lt;/p&gt;

&lt;p&gt;The next step is an agentic benchmark that runs the same scenario against every product.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the runner needs to control
&lt;/h2&gt;

&lt;p&gt;The benchmark is still in build. I am not presenting the following system as finished.&lt;/p&gt;

&lt;p&gt;The runner needs to control five things.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Facts and distractors
&lt;/h3&gt;

&lt;p&gt;A memory test should plant facts at known points, then surround them with realistic conversational noise.&lt;/p&gt;

&lt;p&gt;A draft scenario might include:&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;"fact_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"F07"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"fact"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"My sister's dog is named Pepper"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"introduced_in_session"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"recall_check_session"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"acceptable_answers"&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="s2"&gt;"Pepper"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"distractors"&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;"My coworker's dog is named Milo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"I used to have a cat named Luna"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is not the JSON. It is the separation between what was introduced, when it was introduced, and what counts as a correct answer.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Multi-session behavior
&lt;/h3&gt;

&lt;p&gt;A ten-message chat is not a long-term memory test.&lt;/p&gt;

&lt;p&gt;The runner needs separate sessions, controlled gaps, topic changes, conflicting details, and delayed recall checks. It also needs to distinguish:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;immediate recall&lt;/li&gt;
&lt;li&gt;recall later in the same session&lt;/li&gt;
&lt;li&gt;recall after a new session&lt;/li&gt;
&lt;li&gt;recall after distractors&lt;/li&gt;
&lt;li&gt;false memories that were never introduced&lt;/li&gt;
&lt;li&gt;answers that dodge the question without actually recalling the fact&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Character consistency
&lt;/h3&gt;

&lt;p&gt;Memory and character consistency are related but not identical.&lt;/p&gt;

&lt;p&gt;A character can remember a fact while drifting into a different personality. The evaluator should track changes in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tone and speaking style&lt;/li&gt;
&lt;li&gt;stated biography&lt;/li&gt;
&lt;li&gt;relationship framing&lt;/li&gt;
&lt;li&gt;boundaries and refusals&lt;/li&gt;
&lt;li&gt;first-person identity claims&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A useful benchmark should not reward a model for parroting facts while the character itself falls apart.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Image and video consistency
&lt;/h3&gt;

&lt;p&gt;Media tests need repeated requests, not one cherry-picked result.&lt;/p&gt;

&lt;p&gt;For every app, the runner should request comparable scenes and record:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;generation cost&lt;/li&gt;
&lt;li&gt;latency&lt;/li&gt;
&lt;li&gt;visible identity drift&lt;/li&gt;
&lt;li&gt;clothing and background adherence&lt;/li&gt;
&lt;li&gt;anatomy and artifact failures&lt;/li&gt;
&lt;li&gt;moderation or refusal&lt;/li&gt;
&lt;li&gt;whether the product silently changes the prompt&lt;/li&gt;
&lt;li&gt;all attempts, including failures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The hard part is scoring identity consistency without hiding the judgment inside another black box.&lt;/p&gt;

&lt;p&gt;A vision model can help produce candidate scores, but the benchmark still needs a published rubric, calibration examples, and periodic human review. Otherwise the evaluator simply replaces an opaque product claim with an opaque benchmark claim.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Cost and failure handling
&lt;/h3&gt;

&lt;p&gt;These products use different currencies, retry rules, queues, and subscription limits. Normalizing the marketing price alone is not enough.&lt;/p&gt;

&lt;p&gt;I want to report both the advertised price and the observed cost of completing a fixed test scenario. Failed generations, retries, and timeouts stay in the denominator.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separate the runner from the grader
&lt;/h2&gt;

&lt;p&gt;The testing agent should not grade its own work.&lt;/p&gt;

&lt;p&gt;My current design separates:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;a scenario runner that sends prompts and captures raw artifacts&lt;/li&gt;
&lt;li&gt;a grader that receives the rubric and those artifacts&lt;/li&gt;
&lt;li&gt;an audit layer that preserves prompts, timestamps, screenshots, model/version notes, and scoring explanations&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That separation does not eliminate bias, but it makes errors easier to inspect. If the score looks wrong, a reader can trace it back to a particular artifact and rubric rule.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should be public
&lt;/h2&gt;

&lt;p&gt;A benchmark is not reproducible if readers only see a leaderboard.&lt;/p&gt;

&lt;p&gt;For each published result, I plan to expose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;scenario version&lt;/li&gt;
&lt;li&gt;test date&lt;/li&gt;
&lt;li&gt;product and paid plan&lt;/li&gt;
&lt;li&gt;prompt sequence&lt;/li&gt;
&lt;li&gt;transcript excerpts with privacy-safe redactions&lt;/li&gt;
&lt;li&gt;screenshots and media samples&lt;/li&gt;
&lt;li&gt;number of runs&lt;/li&gt;
&lt;li&gt;success and failure counts&lt;/li&gt;
&lt;li&gt;cost and latency&lt;/li&gt;
&lt;li&gt;scoring rubric&lt;/li&gt;
&lt;li&gt;known limitations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is not to produce a magical objective score. The goal is to make every important judgment inspectable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two questions I have not solved
&lt;/h2&gt;

&lt;p&gt;First, how long should a memory test run before "long-term" means anything? Four sessions may be practical, but practical is not the same as valid.&lt;/p&gt;

&lt;p&gt;Second, what is the least misleading way to score identity consistency across images and video? Embedding similarity is tempting, but it can miss the exact visual drift a human notices immediately.&lt;/p&gt;

&lt;p&gt;If you have built evaluation systems for stateful agents, multimodal models, or long-running conversations, I would value specific criticism of the protocol.&lt;/p&gt;

&lt;p&gt;The current manual methodology and paid-test standard are here: &lt;a href="https://nofilterreview.com/how-we-test/" rel="noopener noreferrer"&gt;https://nofilterreview.com/how-we-test/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I used an AI writing tool to help organize this article. The paid tests, product observations, methodology decisions, and final edits are mine.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>testing</category>
      <category>programming</category>
    </item>
    <item>
      <title>I vibe coded an LM Arena for AI companion apps with Claude Code.</title>
      <dc:creator>Sam</dc:creator>
      <pubDate>Sat, 25 Jul 2026 17:52:04 +0000</pubDate>
      <link>https://dev.to/samm_nofilterreview/i-vibe-coded-an-lm-arena-for-ai-companion-apps-with-claude-code-fbo</link>
      <guid>https://dev.to/samm_nofilterreview/i-vibe-coded-an-lm-arena-for-ai-companion-apps-with-claude-code-fbo</guid>
      <description>&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%2F3mvx47bdm9xyiatvhayj.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%2F3mvx47bdm9xyiatvhayj.png" alt=" " width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I can't write code. I've never written a line of it by hand.&lt;/p&gt;

&lt;p&gt;I still wanted something that didn't exist: a leaderboard for AI companion sites that shows what was tested, when, and how many people voted. Most "Top 10 AI girlfriend apps" pages are affiliate lists with a number stuck on top. LMSYS Chatbot Arena does it properly, so I described that to Claude Code and we built it: &lt;a href="https://nofilterreview.com/rankings/" rel="noopener noreferrer"&gt;seven leaderboards with community voting and dated monthly snapshots&lt;/a&gt;. Astro, Cloudflare Pages Functions, D1. No accounts, no login, no client framework. Voting is a static page and one POST.&lt;/p&gt;

&lt;p&gt;Two things I learned from specifying all of it and writing none of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Every site on the board wants to stuff the vote
&lt;/h2&gt;

&lt;p&gt;They all pay me affiliate commissions, so they all have a reason to send fake votes.&lt;/p&gt;

&lt;p&gt;Ballots over the rate limit don't get rejected. They get written like any other ballot, they just never reach the table the score reads from. If I rejected them I'd have no record, and I couldn't tell you later how many got filtered or why. This way each board can show votes received and votes counted.&lt;/p&gt;

&lt;p&gt;Ratings work the same way. Every submission is kept, and the current state is a derived table with one composite key:&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;ON&lt;/span&gt; &lt;span class="n"&gt;CONFLICT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;voter_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;app_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category_slug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;DO&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;rating_event_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;excluded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rating_event_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updated_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;excluded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;updated_at&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vote again next month and the event is appended and the pointer moves, so nothing counts twice. If I find a cheating pattern later, the raw events are still there to rebuild from.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug that nearly took voting down
&lt;/h2&gt;

&lt;p&gt;The code works. What almost broke it was a link between two things that look unrelated.&lt;/p&gt;

&lt;p&gt;Each rating dimension sent a key to the API, and that key came from slugifying the label on screen. "Filter strength" turned into &lt;code&gt;filter-strength&lt;/code&gt;, which matched. Then I renamed the label for a reason that had nothing to do with the API: a 5 meant "never blocks you", so high was good, but "filter strength" sounds like high means more filtering. It read backwards, so I changed the words.&lt;/p&gt;

&lt;p&gt;Every ballot in that category would have failed with a 400, because &lt;code&gt;spicy&lt;/code&gt; isn't a key the API knows, and nothing would have told me why.&lt;/p&gt;

&lt;p&gt;If you're shipping code you can't read, that's the thing to watch for. Not code that's broken when it lands. Code that works and is wired to a word you'll change later without thinking about it.&lt;/p&gt;

&lt;p&gt;Next is an agent harness that runs the same script against every site and scores the transcripts on memory, character consistency and image consistency, publishing the prompts and the transcripts with each run.&lt;/p&gt;

&lt;p&gt;The boards are live at &lt;a href="https://nofilterreview.com/rankings/" rel="noopener noreferrer"&gt;nofilterreview.com/rankings&lt;/a&gt;. Adult AI sites, so consider that your content warning. Try to break the vote system, I want to know where it gives.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>vibecoding</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
