<?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: Nikolai Fedotenko</title>
    <description>The latest articles on DEV Community by Nikolai Fedotenko (@nfedotenko).</description>
    <link>https://dev.to/nfedotenko</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%2F4182944%2Ff9d8c979-7f8b-416f-b00c-572590893334.jpg</url>
      <title>DEV Community: Nikolai Fedotenko</title>
      <link>https://dev.to/nfedotenko</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nfedotenko"/>
    <language>en</language>
    <item>
      <title>I built a C++ optimizer that trusts neither me nor the LLM</title>
      <dc:creator>Nikolai Fedotenko</dc:creator>
      <pubDate>Sat, 31 Oct 2026 18:45:16 +0000</pubDate>
      <link>https://dev.to/nfedotenko/i-built-a-c-optimizer-that-trusts-neither-me-nor-the-llm-25oi</link>
      <guid>https://dev.to/nfedotenko/i-built-a-c-optimizer-that-trusts-neither-me-nor-the-llm-25oi</guid>
      <description>&lt;p&gt;Tracing JITs have a trick that AOT-compiled C++ never gets to use: they watch the &lt;em&gt;values&lt;/em&gt; flowing through hot code, specialize for them, and guard the assumption with a cheap check. V8 and LuaJIT do this at runtime. I wanted it for a C++ trading system, ahead of time.&lt;/p&gt;

&lt;p&gt;So I built &lt;a href="https://github.com/nfedotenko/pgspec" rel="noopener noreferrer"&gt;pgspec&lt;/a&gt; — a small pipeline that profiles a binary on recorded workload, finds the values that dominate at runtime, and regenerates the build so those values become compile-time constants on the hot path. Guarded, with a generic fallback, and with an LLM in the loop proposing specializations.&lt;/p&gt;

&lt;p&gt;The most important design decision turned out to be this: &lt;strong&gt;nothing in the pipeline is trusted — not the rule-based heuristics, not the LLM, not even my own benchmark.&lt;/strong&gt; Every proposal has to survive a bit-exact differential test and beat the baseline on a benchmark before it ships. That gate ended up rejecting my code, my benchmark methodology (twice), and one of the LLM's ideas. Here's the tour.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pipeline
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;profile (value histograms)  →  proposer (rules | Claude)  →  codegen (specialized.hpp)
        →  bit-exact differential test  →  benchmark gate  →  miss-rate monitor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hot path is a policy template. The generic spec reads everything from the order at runtime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;GenericSpec&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;OrderType&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt;      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kt"&gt;uint32_t&lt;/span&gt;  &lt;span class="n"&gt;lot_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lot_size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt;    &lt;span class="n"&gt;tick_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tick_size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Spec&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kr"&gt;inline&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt; &lt;span class="nf"&gt;process_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Spec&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;lot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lot_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;lots&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;qty&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;lot&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// real division with GenericSpec…&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;odd&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;qty&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;lot&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// … fee switch on spec.type(o), notional on spec.tick_size(o) …&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A profiling build records per-site value histograms — crucially, the &lt;strong&gt;joint distribution of value tuples&lt;/strong&gt;, not per-field marginals. Two fields can each look bimodal while only two of their four combinations ever co-occur, and a guard is a conjunction, so coverage is a property of the joint distribution. The analyzer turns the top tuples into generated specs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// AUTO-GENERATED&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Spec0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;gnu&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;always_inline&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kr"&gt;inline&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="n"&gt;g0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;OrderType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Ioc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="n"&gt;g1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lot_size&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;100u&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="n"&gt;g2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tick_size&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt;
                                       &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;bit_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x3f847ae147ae147bULL&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;g1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;g2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="n"&gt;OrderType&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt;      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;OrderType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Ioc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="kt"&gt;uint32_t&lt;/span&gt;  &lt;span class="n"&gt;lot_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;100u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;    &lt;span class="nf"&gt;tick_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;bit_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x3f847ae147ae147bULL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After inlining, &lt;code&gt;qty / 100&lt;/code&gt; becomes a magic-number multiply, the fee switch folds away, and the tick multiply is a constant. The guard is deliberately &lt;strong&gt;branchless&lt;/strong&gt; — each compare materializes as a flag, combined with bitwise &lt;code&gt;&amp;amp;&lt;/code&gt;, so the whole thing costs one heavily-biased branch. (The naive &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; chain compiled to three data-dependent branches and cost 14% on workloads that drift from the profile.)&lt;/p&gt;

&lt;p&gt;The correctness contract is brutal and simple: the specialized binary must produce &lt;strong&gt;bit-identical&lt;/strong&gt; results to the generic one on any input. Guards select &lt;em&gt;which code path&lt;/em&gt;, never &lt;em&gt;what it computes&lt;/em&gt;. The pipeline enforces this with checksums over both the profiled-like workload and an adversarial one, built with &lt;code&gt;-ffp-contract=off&lt;/code&gt; so FP folding stays IEEE-identical.&lt;/p&gt;

&lt;h2&gt;
  
  
  The LLM in the loop
&lt;/h2&gt;

&lt;p&gt;The fun part. The analyzer has two proposers behind one contract: a rule-based one (top-k joint tuples) and Claude. The model receives the hot-path source plus the profile — including exact bit patterns of observed doubles — and returns specs as structured JSON, under a contract that says, roughly: &lt;em&gt;whenever all your guard terms hold, every accessor must equal the field exactly, or the differential test will reject you.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The trust boundary is explicit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every expression the model returns must tokenize against a &lt;strong&gt;whitelist&lt;/strong&gt; (fields of &lt;code&gt;o&lt;/code&gt;, enum values, numeric literals, &lt;code&gt;std::bit_cast&amp;lt;double&amp;gt;(0x…ULL)&lt;/code&gt;, comparison/arithmetic operators). I tested it with &lt;code&gt;system("rm -rf /")&lt;/code&gt; in a guard term — rejected before codegen.&lt;/li&gt;
&lt;li&gt;Whatever passes is compiled, differential-tested bit-for-bit, and benchmark-gated like any other spec.&lt;/li&gt;
&lt;li&gt;Any failure — no key, invalid JSON, rejected expression, model refusal — degrades to the rule proposer with a warning.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The live run exceeded expectations. The model returned the two correct hot tuples with exact bit patterns, deliberately stopped at two specs ("the remaining ~5% is spread over 12+ tuples at ~0.42% each; each would cost a dispatch branch for well under 1% gain"), warned that upstream-computed tick sizes might differ by 1 ulp from the profiled bits so the guard would safely miss, and suggested checking that via the hit counters — it had noticed the monitoring macros in the source. Its "what I'd try next" list included hoisting dispatch per burst. More on that below, because that idea got rejected — by measurement, not by me.&lt;/p&gt;

&lt;p&gt;This is the same conclusion the community keeps arriving at from the code-review side — see &lt;a href="https://dev.to/tamizuddin/from-vibe-coding-to-verification-the-engineering-case-for-deterministic-checks-in-the-ai-era-1dhe"&gt;"From Vibe-Coding to Verification"&lt;/a&gt; by Tamiz Uddin and &lt;a href="https://dev.to/pangxueyuan2creator/why-ai-generated-code-needs-independent-verification-1j88"&gt;"Why AI-Generated Code Needs Independent Verification"&lt;/a&gt; by pangxueyuan2creator: LLM output is a &lt;em&gt;proposal&lt;/em&gt;, and deterministic verification is what makes it shippable. pgspec just applies that to performance work, where "correct" isn't enough — it also has to be &lt;em&gt;faster&lt;/em&gt;, and that's an empirical question.&lt;/p&gt;

&lt;h2&gt;
  
  
  What measurement actually taught me
&lt;/h2&gt;

&lt;p&gt;Every one of these surprised me, and every one came from the pipeline disagreeing with my expectations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. My first benchmark lied.&lt;/strong&gt; The checksum was a serial FNV chain (&lt;code&gt;xor → mul → xor&lt;/code&gt;), whose ~5-cycle latency &lt;em&gt;was&lt;/em&gt; the critical path. It hid the hot-path cost entirely and "proved" specialization made things slower. Independent accumulators fixed it. The benchmark is part of the product.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. macOS core scheduling produced a 1.9x phantom.&lt;/strong&gt; Identical binaries differed by 1.9x between pipeline runs — P-cores vs E-cores. The fix: interleave the binaries' runs and take min-of-N, so scheduling noise hits everyone alike.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Throughput-bound loops punish this optimization; latency-bound loops reward it.&lt;/strong&gt; With independent iterations, Apple Silicon's out-of-order core hides &lt;code&gt;udiv&lt;/code&gt; latency in an otherwise-idle divider unit, while the magic-multiply replacement competes for shared ALU ports: 0.74x. Put the division on a serial dependency chain (position feedback into the next order's size, like real inventory/risk logic) and the same transform gives 1.17–1.19x. Same code, opposite verdicts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Generated code pushes &lt;em&gt;surrounding&lt;/em&gt; code past inliner thresholds.&lt;/strong&gt; With two specs, the hot-loop lambda silently stopped being inlined, which forced the running position through a store-load on the serial chain. It looked exactly like "multi-spec doesn't pay" (0.84x) until the disassembly showed the outlined call. One &lt;code&gt;always_inline&lt;/code&gt; later: 1.17x, with two specs beating one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Partial specialization is a branch-predictor hack.&lt;/strong&gt; A spec that pins only &lt;code&gt;type == Ioc&lt;/code&gt; (99% of orders) and keeps lot/tick as runtime reads made the &lt;em&gt;adversarial&lt;/em&gt; workload 1.4–1.6x faster: the guard acts as a predictable pre-classifier, splitting one unpredictable 4-way switch into two heavily-biased paths. The rule proposer can't express this; the LLM contract can.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. The LLM's per-burst idea lost — and the gate caught it.&lt;/strong&gt; Hoisting the guard cascade to the head of each instrument burst sounds great, but run membership still has to be verified per order, so you save almost nothing and pay run bookkeeping: 1.13x vs 1.19x for the plain cascade. The idea implicitly assumes a feed-provided dispatch key. Which led to…&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. The dispatch-strategy crossover is a function of spec count.&lt;/strong&gt; With a real instrument id, a generated &lt;code&gt;switch&lt;/code&gt; jumps straight to the instrument's spec and the guard only &lt;em&gt;verifies&lt;/em&gt;. At 2 specs the cascade wins (the switch adds nothing); at 6 Zipf-weighted instruments the keyed dispatch wins (1.10x vs 1.06x, and 11% faster than the cascade in throughput) because cascade cost grows with N while the switch is O(1). Nobody chooses the strategy: the pipeline builds all three binaries and the gate picks per profile.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part I'd defend in a design review
&lt;/h2&gt;

&lt;p&gt;The pipeline ends with an accept/reject gate and a production monitor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gate:&lt;/strong&gt; a specialization (or dispatch strategy) that doesn't beat the alternatives on the primary workload is reverted. It has, so far, rejected a throughput regression, a mis-inlined build, and an LLM-suggested structural change. The proposer suggests; measurement decides.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor:&lt;/strong&gt; the generated header embeds the &lt;em&gt;profiled&lt;/em&gt; generic-path share; a &lt;code&gt;-DPGSPEC_MONITOR&lt;/code&gt; build (~2% overhead, zero when compiled out) counts live guard hits and flags drift when the live miss-rate exceeds twice the expectation. On the profiled workload: 4.98% live vs 5.00% expected. On a shifted one: 95.8%, flagged. A shipped specialization is a bet that the profiled distribution holds — and the day it breaks is exactly the volatile day you care about, so the fallback path has to stay fast, not merely correct.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Instrument-aware LLM proposals (the model already sees the instrument column; it should emit the keyed dispatch table too), Intel PT/LBR sampling instead of instrumented profiling builds, and — once compilers catch up — C++26 reflection (P2996) to generate the probes and Spec structs from annotated types instead of an external Python codegen.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/nfedotenko/pgspec" rel="noopener noreferrer"&gt;github.com/nfedotenko/pgspec&lt;/a&gt;. The whole thing is ~1200 lines including the pipeline, and every number above is reproducible with &lt;code&gt;./pipeline.sh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you're into this corner of the world, &lt;a href="https://dev.to/tapesh_chandradas_5f7919/tickforge-a-low-latency-c20-market-data-order-execution-engine-published-false-tags-cpp-2fek"&gt;TickForge&lt;/a&gt; by Tapesh Chandradas is a nice hand-written counterpart — the manual discipline pgspec tries to automate.&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>performance</category>
      <category>llm</category>
      <category>showdev</category>
    </item>
    <item>
      <title>I built a localization pass that never lets an unreleased game script leave the laptop</title>
      <dc:creator>Nikolai Fedotenko</dc:creator>
      <pubDate>Wed, 14 Oct 2026 21:40:12 +0000</pubDate>
      <link>https://dev.to/nfedotenko/i-built-a-localization-pass-that-never-lets-an-unreleased-game-script-leave-the-laptop-n80</link>
      <guid>https://dev.to/nfedotenko/i-built-a-localization-pass-that-never-lets-an-unreleased-game-script-leave-the-laptop-n80</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week2-2026-10-12"&gt;Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;locsmith&lt;/strong&gt; is a command-line tool that pre-translates a game's string table with an open-weight model running locally, then checks every line before it can break the build.&lt;/p&gt;

&lt;p&gt;I work in game development, and the localization step is where small studios get hurt. The script of an unannounced game is under NDA, so pasting it into a cloud chatbot is off the table. An agency pass on every content update costs more than many indie teams make. And raw machine translation breaks things in ways that only show up in QA: a &lt;code&gt;{price}&lt;/code&gt; placeholder that became &lt;code&gt;{цена}&lt;/code&gt;, a &lt;code&gt;&amp;lt;/color&amp;gt;&lt;/code&gt; tag that disappeared, or a button label that's twice as wide as the button.&lt;/p&gt;

&lt;p&gt;locsmith handles the boring 80% before a human translator sees the file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It batches strings to a local model with context hints ("Blacksmith NPC, gruff but friendly") and the glossary terms relevant to that batch.&lt;/li&gt;
&lt;li&gt;It validates every result: placeholders and rich-text tags must match the source exactly (reordering is allowed), line breaks are preserved, per-string &lt;code&gt;max_len&lt;/code&gt; is respected, and glossary terms are used.&lt;/li&gt;
&lt;li&gt;If a string fails, it goes back to the model with the specific problem ("lost placeholder {price}", "too long: 32 &amp;gt; 28") and is retried. Strings that still fail are marked &lt;code&gt;review&lt;/code&gt; instead of silently shipping.&lt;/li&gt;
&lt;li&gt;Reruns only translate new or changed strings, so it fits a daily-build workflow. A non-zero exit code means "human needed", which lets it gate a CI step.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Who would pay:&lt;/strong&gt; solo devs and 2–10 person studios shipping to Steam in several languages. They'd pay for a one-time license or a small subscription that turns "we'll localize after launch" into "every build has a draft translation and a list of the 20 lines a human must look at".&lt;/p&gt;

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

&lt;p&gt;One command against the bundled sample (13 strings, English → Russian, with a glossary):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python3 locsmith.py samples/strings.csv &lt;span class="nt"&gt;--to&lt;/span&gt; ru &lt;span class="nt"&gt;-g&lt;/span&gt; samples/glossary.ru.csv
&lt;span class="go"&gt;translated 13/13

gemma4:12b @ http://localhost:11434/v1
strings: 13  ok: 11  needs review: 2  auto-fixed on retry: 0  cached: 0
time: 200.2s  (15.40s per new string)
&lt;/span&gt;&lt;span class="gp"&gt;  REVIEW hud.stamina_low: too long: 41 &amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;24 chars
&lt;span class="gp"&gt;  REVIEW shop.buy: too long: 32 &amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;28 chars
&lt;span class="go"&gt;written: samples/strings.ru.csv
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The output CSV is ready to drop into the build; the two &lt;code&gt;review&lt;/code&gt; rows are flagged, not shipped:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;key&lt;/th&gt;
&lt;th&gt;source&lt;/th&gt;
&lt;th&gt;target&lt;/th&gt;
&lt;th&gt;status&lt;/th&gt;
&lt;th&gt;issues&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;shop.buy&lt;/td&gt;
&lt;td&gt;Buy for {price} Aether Shards&lt;/td&gt;
&lt;td&gt;Купить за {price} Осколков Эфира&lt;/td&gt;
&lt;td&gt;review&lt;/td&gt;
&lt;td&gt;too long: 32 &amp;gt; 28 chars&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;shop.not_enough&lt;/td&gt;
&lt;td&gt;You need {0} more Aether Shards.&lt;/td&gt;
&lt;td&gt;Вам нужно еще {0} Осколков Эфира.&lt;/td&gt;
&lt;td&gt;ok&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;loot.picked&lt;/td&gt;
&lt;td&gt;You picked up %d x %s&lt;/td&gt;
&lt;td&gt;Вы подобрали %d x %s&lt;/td&gt;
&lt;td&gt;ok&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;quest.reward&lt;/td&gt;
&lt;td&gt;[b]Reward:[/b] {xp} XP and {shards} Aether Shards&lt;/td&gt;
&lt;td&gt;[b]Награда:[/b] {xp} ОЗП и {shards} Осколков Эфира&lt;/td&gt;
&lt;td&gt;ok&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;npc.smith.greet&lt;/td&gt;
&lt;td&gt;Back again, Warden? ...&lt;/td&gt;
&lt;td&gt;Снова ты, Страж? Кузница не спит, и я тоже.&lt;/td&gt;
&lt;td&gt;ok&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every placeholder (&lt;code&gt;{price}&lt;/code&gt;, &lt;code&gt;{0}&lt;/code&gt;, &lt;code&gt;%d&lt;/code&gt;, &lt;code&gt;%s&lt;/code&gt;, &lt;code&gt;{xp}&lt;/code&gt;, &lt;code&gt;{shards}&lt;/code&gt;, &lt;code&gt;{key_dodge}&lt;/code&gt;), rich-text tag (&lt;code&gt;&amp;lt;color=#ff4040&amp;gt;&lt;/code&gt;, &lt;code&gt;[b]&lt;/code&gt;) and &lt;code&gt;\n&lt;/code&gt; survived, and the glossary terms came through inflected — &lt;em&gt;Осколок Эфира&lt;/em&gt; → &lt;em&gt;Осколков Эфира&lt;/em&gt;, &lt;em&gt;Warden&lt;/em&gt; → &lt;em&gt;Страж&lt;/em&gt;. The only two flags are genuine UI-length overflows a human (or a shorter rephrase) needs to resolve.&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/nfedotenko" rel="noopener noreferrer"&gt;
        nfedotenko
      &lt;/a&gt; / &lt;a href="https://github.com/nfedotenko/locsmith" rel="noopener noreferrer"&gt;
        locsmith
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Local, private, validated pre-translation of game strings with an open-weight model (Gemma via Ollama)
    &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;locsmith&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;Local, private pre-translation of game strings with an open-weight model.&lt;/p&gt;

&lt;p&gt;Indie teams can't send an unannounced game's script to a cloud API (NDAs, leaks) and can't afford
an agency pass on every build. &lt;code&gt;locsmith&lt;/code&gt; runs a first translation pass on your own machine
then &lt;strong&gt;validates every string&lt;/strong&gt; so the output is safe to drop into the build:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;placeholders and tags survive untouched: &lt;code&gt;{0}&lt;/code&gt;, &lt;code&gt;{name}&lt;/code&gt;, &lt;code&gt;%s&lt;/code&gt;, &lt;code&gt;%1$s&lt;/code&gt;, &lt;code&gt;&amp;lt;color=#f00&amp;gt;&lt;/code&gt;, &lt;code&gt;[b]&lt;/code&gt;, &lt;code&gt;\n&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;UI length limits (&lt;code&gt;max_len&lt;/code&gt; per string)&lt;/li&gt;
&lt;li&gt;glossary terms (character names, currencies) used consistently; inflected languages supported via stems, &lt;code&gt;strict&lt;/code&gt; for exact names&lt;/li&gt;
&lt;li&gt;broken strings are sent back to the model with the exact problem, up to &lt;code&gt;--retries&lt;/code&gt; times; the rest are flagged &lt;code&gt;review&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;reruns only translate new or changed strings&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Works with any OpenAI-compatible endpoint: Ollama, llama.cpp &lt;code&gt;llama-server&lt;/code&gt;, vLLM, LM Studio. Standard library only.&lt;/p&gt;
&lt;p&gt;Gemma 4 and other reasoning models "think" before…&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/nfedotenko/locsmith" 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;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model:&lt;/strong&gt; Gemma 4 (&lt;code&gt;gemma4:12b&lt;/code&gt;, Q4_K_M) served by Ollama on a MacBook Pro (Apple M1, 16 GB). The tool talks to any OpenAI-compatible endpoint, so llama.cpp's &lt;code&gt;llama-server&lt;/code&gt;, vLLM or LM Studio work too. Switching models is a single flag.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reasoning off by default.&lt;/strong&gt; Gemma 4 is a thinking model: left alone it spent ~1,700–3,000 tokens &lt;em&gt;reasoning&lt;/em&gt; about a 13-string batch before emitting any JSON, which on a 16 GB laptop meant minutes per batch. locsmith sends &lt;code&gt;reasoning_effort: none&lt;/code&gt;, which drops the same batch to ~310 generated tokens with identical output quality — about a 10x cut. &lt;code&gt;--reasoning low|medium|high&lt;/code&gt; turns deliberation back on when you want it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero dependencies:&lt;/strong&gt; a single Python file using only the standard library, so a studio's build machine doesn't need another package manager.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation is the product.&lt;/strong&gt; A single regex tokenizer recognizes &lt;code&gt;{0}&lt;/code&gt;/&lt;code&gt;{name}&lt;/code&gt;, printf (&lt;code&gt;%s&lt;/code&gt;, &lt;code&gt;%1$s&lt;/code&gt;, &lt;code&gt;%.2f&lt;/code&gt;), HTML-like tags (&lt;code&gt;&amp;lt;color=#f00&amp;gt;&lt;/code&gt;), BBCode (&lt;code&gt;[b]&lt;/code&gt;) and literal escapes. Source and translation are compared as multisets, so a language can move &lt;code&gt;%s&lt;/code&gt; before &lt;code&gt;%d&lt;/code&gt; but can't lose or invent one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Glossary in inflected languages:&lt;/strong&gt; in Russian, "Aether Shard" can appear as &lt;em&gt;осколок&lt;/em&gt;, &lt;em&gt;осколков&lt;/em&gt; or &lt;em&gt;осколками&lt;/em&gt;. Exact matching produces endless false alarms, so by default each glossary word is matched by its stem. Proper names can be marked &lt;code&gt;strict&lt;/code&gt; instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tests&lt;/strong&gt; cover the validators, JSON recovery from chatty model output, the retry loop and the incremental cache.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Results on the bundled sample (13 strings, en → ru, &lt;code&gt;gemma4:12b&lt;/code&gt; via Ollama):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;passed validation on the first try: 11 / 13&lt;/li&gt;
&lt;li&gt;auto-fixed on retry: 0&lt;/li&gt;
&lt;li&gt;left for human review: 2 (both genuine UI-length overflows, not model mistakes)&lt;/li&gt;
&lt;li&gt;time: 200 s total, ~15 s/string on a 16 GB M1 — memory-bound, since a 12B model barely fits; the first-pass batch of all 13 runs in ~31 s warm, and the retries for the two length failures dominate the rest. A machine with more RAM, or a smaller Gemma, is considerably faster.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tiger Data: asking the catalog a question
&lt;/h2&gt;

&lt;p&gt;The translator stays on the laptop. After a pass, a loc lead still needs a dashboard question: &lt;em&gt;which Russian UI strings actually overflow &lt;code&gt;max_len&lt;/code&gt;, and which keys carry glossary terms?&lt;/em&gt; That is ordinary SQL, not another LLM call, so I loaded the public sample catalog onto a Tiger Cloud TimescaleDB service (&lt;code&gt;locsmith-dev&lt;/code&gt;: PostgreSQL 18.6, TimescaleDB 2.30.2) through the Tiger MCP and asked it there.&lt;/p&gt;

&lt;p&gt;The overflow query returned in ~200 ms and agreed with locsmith's &lt;code&gt;review&lt;/code&gt; flags — independently, without dumping rows into chat:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;key&lt;/th&gt;
&lt;th&gt;Russian&lt;/th&gt;
&lt;th&gt;max&lt;/th&gt;
&lt;th&gt;chars&lt;/th&gt;
&lt;th&gt;over by&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hud.stamina_low&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Мало выносливости! (+ color tags)&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;41&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+17&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;shop.buy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Купить за &lt;code&gt;{price}&lt;/code&gt; Осколки Эфира&lt;/td&gt;
&lt;td&gt;28&lt;/td&gt;
&lt;td&gt;31&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+3&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;achv.first_blood&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Первая кровь&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;fits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ui.play&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Играть&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;fits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ui.settings&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Настройки&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;fits&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A join against the glossary table showed coverage too: &lt;em&gt;Aether Shard&lt;/em&gt; → Осколок Эфира on &lt;code&gt;shop.buy&lt;/code&gt;, &lt;code&gt;shop.not_enough&lt;/code&gt;, &lt;code&gt;quest.reward&lt;/code&gt;; &lt;em&gt;Stamina&lt;/em&gt; on the HUD and tutorial; &lt;em&gt;Warden&lt;/em&gt; (&lt;code&gt;strict&lt;/code&gt;) on the blacksmith greeting; &lt;em&gt;Hollow&lt;/em&gt; on the achievement description.&lt;/p&gt;

&lt;p&gt;The unreleased script never goes to Tiger. Only keys, lengths, status, and glossary stems — the same metadata a studio already puts in a loc dashboard. TimescaleDB is still Postgres here; hypertables would matter later for per-build translation runs as time series. That split is the product: Gemma translates privately, Tiger answers the catalog.&lt;/p&gt;

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

&lt;p&gt;For this product, an open model is a requirement, not just a preference.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Data never leaves the machine.&lt;/strong&gt; An unannounced game's dialogue, item names and plot twists are what the NDA protects. With a local open-weight model, the leak surface is the developer's own laptop. I can say that to a studio with a straight face. "We send it to an API, but they promise not to train on it" doesn't pass a publisher's legal review.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The unit economics work.&lt;/strong&gt; A mid-size indie game has tens of thousands of strings, and every patch adds more. With local inference, the marginal cost of a rerun is electricity. That's what makes "translate every build" a feature instead of a budget meeting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No lock-in, room to specialize.&lt;/strong&gt; Games have their own vocabulary and tone. With open weights, a studio can swap in a bigger model for the story and a fast one for UI, or fine-tune on its previously approved translations. Nobody can deprecate the model under them mid-production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Works offline.&lt;/strong&gt; Build machines, trains, conventions, game jams.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The trade-off is that a local 12B model makes more mistakes than a frontier API. That's exactly why the validation and retry loop exists: the tool doesn't trust the model, it checks it. And why the catalog lives in Postgres after the pass: the questions a loc lead asks ("what overflowed this build?") should be SQL, not another prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Best Use of Gemma&lt;/li&gt;
&lt;li&gt;Best Use of Tiger Data&lt;/li&gt;
&lt;/ul&gt;

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