<?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: xmmyyx</title>
    <description>The latest articles on DEV Community by xmmyyx (@xmmyyx).</description>
    <link>https://dev.to/xmmyyx</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%2F4200045%2Ffb0edf45-d3ad-4e9f-a977-c9b9d2089a04.png</url>
      <title>DEV Community: xmmyyx</title>
      <link>https://dev.to/xmmyyx</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/xmmyyx"/>
    <language>en</language>
    <item>
      <title>Why I Added a Validator to a Small JSONL Candidate Ranker</title>
      <dc:creator>xmmyyx</dc:creator>
      <pubDate>Thu, 22 Oct 2026 08:54:08 +0000</pubDate>
      <link>https://dev.to/xmmyyx/why-i-added-a-validator-to-a-small-jsonl-candidate-ranker-43c7</link>
      <guid>https://dev.to/xmmyyx/why-i-added-a-validator-to-a-small-jsonl-candidate-ranker-43c7</guid>
      <description>&lt;p&gt;A text similarity score can look perfectly reasonable even when it's wrong.&lt;/p&gt;

&lt;p&gt;Suppose a script ranks a set of candidate records and writes the results to JSONL. Another step reads that file, transforms it, or passes it downstream. How do we know the saved scores and rankings still agree with the original inputs?&lt;/p&gt;

&lt;p&gt;That's the problem I explored with &lt;strong&gt;JSONL Candidate Align&lt;/strong&gt;, a small open-source Python CLI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/xmmyyx/jsonl-candidate-align" rel="noopener noreferrer"&gt;https://github.com/xmmyyx/jsonl-candidate-align&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Two commands, two responsibilities
&lt;/h2&gt;

&lt;p&gt;The tool separates candidate generation from output validation.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;jsonl-align align&lt;/code&gt; reads already-structured query and candidate records. It uses character trigrams and numeric overlap to calculate lexical scores, applies type compatibility and admission rules, and writes up to eight candidates per query.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;jsonl-align validate&lt;/code&gt; reads the original inputs and a saved candidate result. It recomputes the lexical reference and checks candidate eligibility, metrics, candidate sets, and ranks.&lt;/p&gt;

&lt;p&gt;The generated output contains identifiers, component scores, ranks, and caller-supplied page references. The comparison output helps identify records whose saved values no longer match the implemented rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small synthetic example
&lt;/h2&gt;

&lt;p&gt;The README includes a minimal example using these two JSONL records.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;queries.jsonl&lt;/strong&gt;&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="nl"&gt;"blind_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"DEMO-Q1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"blind_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"SC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"comparable_text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"cobalt sensor 314159"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;candidates.jsonl&lt;/strong&gt;&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="nl"&gt;"holdout_unit_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"DEMO-C1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"holdout_unit_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"M3_AUTO_KEEP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"comparable_text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"cobalt sensor 314159"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"pages"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installing the project from source with Python 3.11 or later, the two commands are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;jsonl-align&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;align&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--queries&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;queries.jsonl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--candidates&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;candidates.jsonl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--output-dir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;alignment-output&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;jsonl-align&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;validate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--queries&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;queries.jsonl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--candidates&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;candidates.jsonl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--actual&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;alignment-output/candidate_alignment_rows.jsonl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--output-dir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;validation-output&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the recorded synthetic test, the generated candidate had a score of &lt;code&gt;1.0&lt;/code&gt; and rank &lt;code&gt;1&lt;/code&gt;. Validation returned &lt;code&gt;PASS&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then I examined a controlled modification: copying the generated candidate file and changing only its saved &lt;code&gt;score&lt;/code&gt; from &lt;code&gt;1.0&lt;/code&gt; to &lt;code&gt;0.5&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The original inputs, original output, and rank were unchanged.&lt;/p&gt;

&lt;p&gt;When the copied file was validated against the same inputs, validation returned &lt;code&gt;FAIL&lt;/code&gt;, with &lt;code&gt;SCORE_DRIFT&lt;/code&gt; among the reported difference classes. The CLI exited with code &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is a deliberately narrow test, but it shows why keeping the validation step separate from candidate generation can be useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the validator does not prove
&lt;/h2&gt;

&lt;p&gt;There is an important distinction here.&lt;/p&gt;

&lt;p&gt;Generation and validation share the same scoring implementation. Validation uses exact Decimal comparisons for output scores, while scoring itself uses floating-point arithmetic.&lt;/p&gt;

&lt;p&gt;A passing result therefore confirms agreement with the implemented lexical contract for the supplied inputs. It is not an independent proof that the scoring algorithm is defect-free.&lt;/p&gt;

&lt;p&gt;Nor does lexical similarity establish semantic correctness.&lt;/p&gt;

&lt;p&gt;The tool also expects callers to supply structured and typed JSONL records. It does not parse PDF or Word documents, run OCR, or make compliance decisions. Candidate pool limits and input order can affect the results, and large-corpus performance has not been established.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why publish it?
&lt;/h2&gt;

&lt;p&gt;I wanted a small, inspectable example of how candidate ranking, structured output, and result validation can fit into a Python CLI workflow.&lt;/p&gt;

&lt;p&gt;The source code separates command handling, scoring, and comparison logic. The README documents the input/output contract and provides an entirely synthetic Quickstart.&lt;/p&gt;

&lt;p&gt;The project is available under the MIT License:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/xmmyyx/jsonl-candidate-align" rel="noopener noreferrer"&gt;https://github.com/xmmyyx/jsonl-candidate-align&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'd be interested in feedback from developers who work with JSONL pipelines: when validating saved candidate results, which types of mismatches are most useful to report?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Project by Qi Zheng. Copyright (c) 2026 Qi Zheng. MIT License.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI disclosure: ChatGPT assisted with drafting and editing this article. The technical claims and synthetic demonstration are based on the project's published source and recorded test results. The author is responsible for reviewing the content before publication.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>cli</category>
      <category>showdev</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
