<?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: Mohsen Seyedkazemi Ardebili</title>
    <description>The latest articles on DEV Community by Mohsen Seyedkazemi Ardebili (@mskazemi).</description>
    <link>https://dev.to/mskazemi</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%2F3997511%2Ff85584b3-05ee-47b7-b358-34484bbd147a.png</url>
      <title>DEV Community: Mohsen Seyedkazemi Ardebili</title>
      <link>https://dev.to/mskazemi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mskazemi"/>
    <language>en</language>
    <item>
      <title>Whisper typed its own prompt into my document</title>
      <dc:creator>Mohsen Seyedkazemi Ardebili</dc:creator>
      <pubDate>Tue, 03 Nov 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/mskazemi/whisper-typed-its-own-prompt-into-my-document-589c</link>
      <guid>https://dev.to/mskazemi/whisper-typed-its-own-prompt-into-my-document-589c</guid>
      <description>&lt;p&gt;If you use &lt;code&gt;faster-whisper&lt;/code&gt; or &lt;code&gt;openai-whisper&lt;/code&gt; with an &lt;code&gt;initial_prompt&lt;/code&gt;, try this sometime. Record two seconds of a fan, or a keyboard, or nothing in particular, loud enough to pass whatever loudness check you have. Transcribe it.&lt;/p&gt;

&lt;p&gt;There is a decent chance you get your prompt back.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://github.com/MSKazemi/yazses" rel="noopener noreferrer"&gt;YazSes&lt;/a&gt;, the offline dictation tool I work on, the built-in prompt is one short sentence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;BUILTIN_PROMPT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The app is called YazSes.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is there so Whisper spells the app's name right. And on a noisy burst with no speech in it, Whisper would sometimes transcribe it as... "The app is called YazSes." Typed straight into whatever window you had open. (&lt;a href="https://github.com/MSKazemi/yazses/issues/598" rel="noopener noreferrer"&gt;#598&lt;/a&gt; has the details.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this happens
&lt;/h2&gt;

&lt;p&gt;Whisper treats &lt;code&gt;initial_prompt&lt;/code&gt; as if it were the text that came just before the audio. My working explanation (a hypothesis, not something I have proven inside the model) is that when the audio carries no words, the easiest thing for the decoder to produce is the text it was just shown. Loud noise is the worst case: a quiet clip gets dropped by the energy gate before Whisper sees it, but a loud one gets through.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the obvious fix doesn't work
&lt;/h2&gt;

&lt;p&gt;Whisper reports a &lt;code&gt;no_speech_prob&lt;/code&gt; for each segment. Surely you just drop segments where it is high?&lt;/p&gt;

&lt;p&gt;I measured it, with the &lt;code&gt;small&lt;/code&gt;, &lt;code&gt;base.en&lt;/code&gt; and &lt;code&gt;tiny.en&lt;/code&gt; models on synthetic noise bursts (white, brown and low-frequency noise and clicks, half a second to three seconds). On the echoes, &lt;code&gt;no_speech_prob&lt;/code&gt; ranged from &lt;strong&gt;0.43 to 0.97&lt;/strong&gt;. On a public-domain speech sample it was &lt;strong&gt;0.02 to 0.27&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Two problems with cutting on that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;faster-whisper's default &lt;code&gt;no_speech_threshold&lt;/code&gt; is 0.6. Every echo between 0.43 and 0.6 sails straight through the default settings.&lt;/li&gt;
&lt;li&gt;The gap between 0.27 and 0.43 comes from one speech sample and some synthetic noise. I would not bet a user's words on a cutoff placed inside a gap that narrow, measured that thinly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And it wasn't only the built-in prompt. A personal vocabulary list came back too: "Ydotool, ydotool, Mohsen, Wayland." Whatever you prime the model with is what it reaches for when there is nothing to hear.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: ask a different model
&lt;/h2&gt;

&lt;p&gt;The fix is to stop asking Whisper whether there was speech, and ask an independent detector &lt;em&gt;before&lt;/em&gt; decoding.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;faster-whisper&lt;/code&gt; already bundles &lt;a href="https://github.com/snakers4/silero-vad" rel="noopener noreferrer"&gt;Silero VAD&lt;/a&gt;, so it costs no new dependency. Before a burst is decoded, YazSes asks Silero whether it holds any speech. If Silero positively says no, the burst is discarded and logged as &lt;code&gt;discard_reason = "no_speech"&lt;/code&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%2F9x70t0g5h8mcewrel5u1.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%2F9x70t0g5h8mcewrel5u1.png" alt="Energy gate, then Silero VAD; only a positive 'no speech' discards the burst" width="800" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The most important design decision is the direction it fails in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;holds_no_speech&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sample_rate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;detector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Detector&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;True only when the detector *positively* found no speech. Never raises.

    ``None`` from the detector (unavailable, wrong sample rate) and any exception both
    read as &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cannot tell&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, which is ``False``: the burst is decoded.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;verdict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;detector&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;_default_detector&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sample_rate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# noqa: BLE001 - a gate must never break dictation
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;verdict&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It &lt;strong&gt;fails open&lt;/strong&gt;. If Silero can't run, can't load, or gets audio at the wrong rate, the burst is decoded exactly as before. Think about the costs: a phantom sentence is annoying and you delete it. Silently swallowing words the user actually said is much worse, because they may not notice until later. A gate that guards against the small error must never cause the big one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the threshold
&lt;/h2&gt;

&lt;p&gt;Silero's default speech threshold is 0.5. I used &lt;strong&gt;0.3&lt;/strong&gt;, and here is the evidence, including its limits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I cut 22 single-word bursts from the public-domain JFK speech sample and made 27 seeded noise bursts.&lt;/li&gt;
&lt;li&gt;At 0.3: 0 words dropped, 0 noise bursts passed.&lt;/li&gt;
&lt;li&gt;At 0.5: 2 quiet words were lost.&lt;/li&gt;
&lt;li&gt;With a crude "whisper" proxy (random-phase audio), 3 of 22 words were lost at 0.3.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is synthetic audio and a small set. I have &lt;strong&gt;not&lt;/strong&gt; measured it on real whispered, accented or dysarthric speech, and those are exactly the users a dictation tool should care about most. So the check is skipped in the modes built for them: whispered/low-effort mode, the dysfluency-friendly mode and the quiet sotto-voce channel. It can also be turned off:&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;[accessibility]&lt;/span&gt;
&lt;span class="py"&gt;speech_presence_check&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;   &lt;span class="c"&gt;# the default since v2.43.2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What I took away
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Loudness can't tell a person from a fan.&lt;/strong&gt; An energy gate is a first filter, not a speech detector.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't ask the model that is failing whether it is failing.&lt;/strong&gt; Its own &lt;code&gt;no_speech_prob&lt;/code&gt; let echoes through at default settings. A second, independent model that never saw the prompt answers the question more cleanly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decide which way a gate should fail before you write it.&lt;/strong&gt; Then write the test for the failure, not just the happy path.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you run Whisper with a prompt in any app (meeting notes, voice notes, subtitles), check what it returns on two seconds of loud noise. It takes a minute and you may be surprised.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The fix shipped in YazSes v2.43.2 (&lt;a href="https://github.com/MSKazemi/yazses/blob/main/docs/releases/v2.43.2.md" rel="noopener noreferrer"&gt;release notes&lt;/a&gt;). YazSes is offline, open source and Apache-2.0: &lt;a href="https://github.com/MSKazemi/yazses" rel="noopener noreferrer"&gt;github.com/MSKazemi/yazses&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I wrote this with help from an AI assistant for drafting and editing; the bug, the measurements and the design choices are mine, and I checked every fact against the code and release notes.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>machinelearning</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>My dictation app typed words I never said. The silence gate was quieter than my room.</title>
      <dc:creator>Mohsen Seyedkazemi Ardebili</dc:creator>
      <pubDate>Thu, 29 Oct 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/mskazemi/my-dictation-app-typed-words-i-never-said-the-silence-gate-was-quieter-than-my-room-26d0</link>
      <guid>https://dev.to/mskazemi/my-dictation-app-typed-words-i-never-said-the-silence-gate-was-quieter-than-my-room-26d0</guid>
      <description>&lt;p&gt;One afternoon my own dictation tool started typing sentences I had not said.&lt;/p&gt;

&lt;p&gt;Not garbage. Fluent, confident, plausible English. If you have used Whisper, you may already know where this is going.&lt;/p&gt;

&lt;h2&gt;
  
  
  Whisper does not do "nothing"
&lt;/h2&gt;

&lt;p&gt;Speech models like Whisper are trained to produce text. Hand one a clip of an empty room, and it will very often give you words anyway, a phrase it has seen a thousand times, or something that sounds like a polite closing line. This is well known, and the usual defence is simple: &lt;strong&gt;don't send it silence&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://github.com/MSKazemi/yazses" rel="noopener noreferrer"&gt;YazSes&lt;/a&gt; (an offline hold-to-talk dictation tool I maintain) the first defence is an energy gate. When you release the key, it computes the mean absolute level of the recording. If that is below a threshold, the clip is dropped and Whisper never sees 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;[accessibility]&lt;/span&gt;
&lt;span class="py"&gt;vad_threshold&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;   &lt;span class="c"&gt;# the default&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My own config said &lt;code&gt;0.0005&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;My room's noise floor, measured, was around &lt;code&gt;0.0039&lt;/code&gt;. The gate sat almost eight times &lt;em&gt;below&lt;/em&gt; the room. Every time I pressed the key without speaking, or paused before talking, the fan and the street went straight through to Whisper, and Whisper wrote me some prose.&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%2Fb6aw1rdq6w9uisfe67pl.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%2Fb6aw1rdq6w9uisfe67pl.png" alt="The energy gate: speech, room noise, and where a threshold must sit" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Three things that went wrong at once
&lt;/h2&gt;

&lt;p&gt;It is tempting to blame "a bad config value". But three separate things had to fail for me to &lt;em&gt;not notice&lt;/em&gt; for a while.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The value was below the noise.&lt;/strong&gt; I don't actually know how &lt;code&gt;0.0005&lt;/code&gt; got into my config. It does not matter much. Any threshold that is not measured against the room it runs in is a guess.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The warning that should have caught it was silent.&lt;/strong&gt; &lt;code&gt;yazses doctor --mic&lt;/code&gt; measured the room and compared it with the threshold. In exactly this case, it printed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[OK] Mic level: ambient 0.0010 under vad_threshold 0.0005
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read that twice. 0.0010 is not under 0.0005.&lt;/p&gt;

&lt;p&gt;The warning only fired when the room was "not silent", and "silent" was judged against a &lt;em&gt;fixed&lt;/em&gt; floor of 0.002 that had nothing to do with my gate. So for any threshold below 0.002, the warning was switched off across exactly the band where it was needed. The one check designed for this situation reported success. (Fixed back in v2.25.1. It now says, plainly, that silence will pass the gate and be transcribed as invented words.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The calibrator measured the wrong thing.&lt;/strong&gt; There was a &lt;code&gt;mic-level&lt;/code&gt; command to pick a threshold for you. It recorded &lt;em&gt;one&lt;/em&gt; clip and assumed you were speaking in it. If you weren't, it calibrated to the room. In four empty-room recordings it measured 0.0036 to 0.0050 and recommended 0.002 to 0.0025: below the noise, every single time.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to calibrate a gate properly
&lt;/h2&gt;

&lt;p&gt;The fix is boring, and that is the point.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Measure the room and the voice separately.&lt;/strong&gt; Since v2.36.0, &lt;code&gt;yazses mic-level&lt;/code&gt; records a few seconds of silence first, then a few seconds of you talking. Now there are two numbers, and the gate must sit between them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;lo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_MIN_THRESHOLD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;room&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# comfortably above the room
&lt;/span&gt;&lt;span class="n"&gt;hi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;speech&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;                       &lt;span class="c1"&gt;# comfortably below your voice
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Refuse when the numbers are too close.&lt;/strong&gt; If &lt;code&gt;hi &amp;lt; lo&lt;/code&gt;, the voice and the room are less than 3x apart. At that point any threshold you pick will either let noise through or eat quiet words. The honest output is not a number. It is "I can't calibrate this, move the mic or lower the gain." So it refuses.&lt;/p&gt;

&lt;p&gt;That 3x is not tuned, by the way. It falls out of the two margins (1.5 / 0.5). For reference, across 1,646 real dictation events, speech had a median level of 0.0394 and clips that passed the gate but produced no text had a median of 0.0069, about 5.7x apart. Plenty of room, &lt;em&gt;if&lt;/em&gt; you measure it.&lt;/p&gt;

&lt;p&gt;The next release goes one step further (already on &lt;code&gt;main&lt;/code&gt;): it aims the gate at twice the room level instead of halfway between room and voice, and it refuses recordings that are too short to say anything (under one second of room or two seconds of speech). That came from a bug report that &lt;code&gt;mic-level --set&lt;/code&gt; picked a different threshold on every run.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I took away
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A threshold is meaningless without the noise it is meant to reject.&lt;/strong&gt; Measure the noise, in place, on the real device.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One sample cannot tell you whether anyone spoke in it.&lt;/strong&gt; If your calibration depends on the user doing something, record that something separately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When two measurements are too close, refuse.&lt;/strong&gt; A confident wrong number is worse than "I can't".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test your warnings in the case they exist for.&lt;/strong&gt; My warning was fine in every case except the one it was written for.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you dictate with Whisper (any tool, not just mine) and you see phantom text, check your silence gate before you blame the model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yazses mic-level          &lt;span class="c"&gt;# measures room, then voice, and suggests a threshold&lt;/span&gt;
yazses mic-level &lt;span class="nt"&gt;--set&lt;/span&gt;    &lt;span class="c"&gt;# writes it to your config&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;YazSes is offline, open source (Apache-2.0), and runs on Linux, macOS and Windows: &lt;a href="https://github.com/MSKazemi/yazses" rel="noopener noreferrer"&gt;github.com/MSKazemi/yazses&lt;/a&gt;. How the gate works in detail: &lt;a href="https://github.com/MSKazemi/yazses/blob/main/docs/how-to/silent-audio-discarding.md" rel="noopener noreferrer"&gt;silent audio discarding&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I wrote this with help from an AI assistant for drafting and editing; the incident, the numbers and the conclusions are mine, and I checked each fact against the code and changelog.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>machinelearning</category>
      <category>linux</category>
      <category>debugging</category>
    </item>
    <item>
      <title>Can an AI agent's decision produce a verifiable receipt? An architecture proposal</title>
      <dc:creator>Mohsen Seyedkazemi Ardebili</dc:creator>
      <pubDate>Wed, 28 Oct 2026 00:45:01 +0000</pubDate>
      <link>https://dev.to/mskazemi/can-an-ai-agents-decision-produce-a-verifiable-receipt-an-architecture-proposal-12do</link>
      <guid>https://dev.to/mskazemi/can-an-ai-agents-decision-produce-a-verifiable-receipt-an-architecture-proposal-12do</guid>
      <description>&lt;p&gt;Explore how Jev and NovaFabric could support verifiable AI agent decision receipts, policy checks, and replayable execution evidence. This is a research proposal, not a shipped integration.&lt;/p&gt;

&lt;p&gt;Imagine an AI agent working on a data-processing task.&lt;/p&gt;

&lt;p&gt;At some point, it decides to upload a file to an external endpoint. The operation succeeds, and the workflow continues.&lt;/p&gt;

&lt;p&gt;Six months later, someone asks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why did the agent decide to upload that file?&lt;/li&gt;
&lt;li&gt;What information was available when the decision was made?&lt;/li&gt;
&lt;li&gt;Which model evaluated the action?&lt;/li&gt;
&lt;li&gt;Was the action authorized?&lt;/li&gt;
&lt;li&gt;Can we verify the original record without trusting the running system?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A successful HTTP response cannot answer those questions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if every important AI-agent decision could produce a verifiable receipt?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is a research direction I'm exploring around two technologies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://typesafe.ai/blog/introducing-system-one-models-and-jev" rel="noopener noreferrer"&gt;&lt;strong&gt;Jev&lt;/strong&gt;&lt;/a&gt;, a System One model from TypeSafe AI designed for fast, structured decisions.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/MSKazemi/novafabric" rel="noopener noreferrer"&gt;&lt;strong&gt;NovaFabric&lt;/strong&gt;&lt;/a&gt;, an open-source system I've been developing to capture, replay, compare, and verify AI-agent execution evidence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The idea is not to connect two APIs and call it an integration.&lt;/p&gt;

&lt;p&gt;It's to investigate how we might preserve the evidence behind an AI decision so that engineers, researchers, and auditors can inspect it later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Current status:&lt;/strong&gt; This is an architectural proposal. The Jev integration described here has not been shipped or benchmarked, and there is no announced partnership with TypeSafe AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Why Jev is interesting
&lt;/h2&gt;

&lt;p&gt;Many AI-agent decisions don't require an essay.&lt;/p&gt;

&lt;p&gt;An agent might need to answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is this tool call consistent with the assigned task?&lt;/li&gt;
&lt;li&gt;Which risk category best describes an operation?&lt;/li&gt;
&lt;li&gt;Does this answer satisfy a particular evaluation criterion?&lt;/li&gt;
&lt;li&gt;Does a delegated action appear to exceed the original task?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Traditional LLMs can answer these questions, but generating explanatory text is not always the right interface.&lt;/p&gt;

&lt;p&gt;TypeSafe AI's Jev exposes three useful primitives:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Primitive&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Choice&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Select from predefined options&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Score&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Evaluate something against a defined rubric&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Noul&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Return a probability for a yes/no proposition&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For example, we might ask Jev whether an agent's proposed file transfer appears unrelated to its assigned task.&lt;/p&gt;

&lt;p&gt;But there is a critical distinction:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A model's probability is not proof that an action is safe, unsafe, or authorized.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is a signal that must be evaluated against real outcomes and combined with independently enforced rules.&lt;/p&gt;

&lt;p&gt;That's where the next part becomes interesting.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. What NovaFabric adds
&lt;/h2&gt;

&lt;p&gt;NovaFabric focuses on something different from model inference: preserving agent execution evidence.&lt;/p&gt;

&lt;p&gt;Its central artifact is a &lt;strong&gt;Run Capsule&lt;/strong&gt;, a portable directory containing recorded execution information from supported capture surfaces.&lt;/p&gt;

&lt;p&gt;Depending on the workload, that includes model calls, tool calls, execution traces, environment information, and provenance.&lt;/p&gt;

&lt;p&gt;NovaFabric supports structural comparison and scoped replay. Capsules can also be cryptographically sealed using an operator-configured key, making later modifications detectable.&lt;/p&gt;

&lt;p&gt;If you want to try the verification side today, this tutorial walks through it end to end: &lt;a href="https://novafabric.ai/docs/tutorials/prove-a-run-to-an-auditor/" rel="noopener noreferrer"&gt;Verify a sealed AI agent run for an auditor, offline&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This matters because an AI decision has two distinct questions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Was the judgment useful or correct?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That requires evaluation against evidence and outcomes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is the archived decision record unchanged?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That can be checked using cryptographic integrity mechanisms, subject to their trust assumptions.&lt;/p&gt;

&lt;p&gt;These are different problems. We should not confuse them.&lt;/p&gt;

&lt;p&gt;For more background, see my arXiv preprint:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://arxiv.org/abs/2609.12582" rel="noopener noreferrer"&gt;&lt;strong&gt;NovaFabric: Tamper-Evident, Replayable Evidence for Autonomous AI Agent Runs&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The idea: a Decision Witness
&lt;/h2&gt;

&lt;p&gt;I'm considering an optional component called a &lt;strong&gt;Decision Witness&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think of it as a versioned record that connects an agent action with the information used to assess and authorize it.&lt;/p&gt;

&lt;p&gt;A proposed record could contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An identifier for the tool call or agent action.&lt;/li&gt;
&lt;li&gt;The model and version used for an assessment.&lt;/li&gt;
&lt;li&gt;The question template and relevant input digest.&lt;/li&gt;
&lt;li&gt;The model's structured output and probability distribution.&lt;/li&gt;
&lt;li&gt;The applicable policy and authorization result.&lt;/li&gt;
&lt;li&gt;Human approval information, when relevant.&lt;/li&gt;
&lt;li&gt;A reference to the captured execution evidence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's an illustrative schema:&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;"schema_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"decision-witness/0.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;"event_ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tool-call-42"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"assessment"&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="nl"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"typesafe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"model_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;"&amp;lt;pinned-jev-version&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"question_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;"off_task_transfer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"noul"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"probability"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.87&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="nl"&gt;"authorization"&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="nl"&gt;"policy_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;"outbound-policy-v3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outcome"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"review_required"&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="nl"&gt;"human_approval"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pending"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"origin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"observed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"evidence_ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha256:&amp;lt;digest&amp;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;&lt;em&gt;This is a hypothetical design example, not an implemented NovaFabric schema or a real Jev response. The probability is illustrative.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The important feature isn't the number &lt;code&gt;0.87&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It's the ability to revisit the evidence, policy, and decision that accompanied it.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. How could the architecture work?
&lt;/h2&gt;

&lt;p&gt;A possible architecture:&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%2Fwno4vlrnkbxozk7pqctl.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%2Fwno4vlrnkbxozk7pqctl.png" alt="Conceptual architecture of the proposed Decision Witness: an agent action is validated and minimized, optionally assessed by Jev as an advisory signal, then passed through a deterministic policy gate and human approval, recorded as a Decision Witness in a NovaFabric evidence record, and used for replay, diff and verify" width="800" height="568"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Conceptual architecture: proposed, not implemented. Policy decides; Jev only advises; the witness records both.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The exact enforcement boundary matters.&lt;/p&gt;

&lt;p&gt;Jev would not be allowed to override identity permissions, data policies, or hard authorization constraints.&lt;/p&gt;

&lt;p&gt;A policy engine would remain responsible for those checks.&lt;/p&gt;

&lt;p&gt;The Decision Witness would preserve the observed assessment and policy outcome. For post-run assessments, a separately linked attestation would avoid silently modifying an already sealed capsule.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Four experiments I'd like to explore
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Experiment A: Detect unusual agent behavior
&lt;/h3&gt;

&lt;p&gt;Take a collection of recorded agent tool calls, including benign and suspicious scenarios.&lt;/p&gt;

&lt;p&gt;Run a Jev-based assessment in shadow mode.&lt;/p&gt;

&lt;p&gt;Compare it against deterministic rules and NovaFabric's existing experimental security/provenance anomaly detector.&lt;/p&gt;

&lt;p&gt;Research questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does Jev identify useful semantic anomalies that structural detection misses?&lt;/li&gt;
&lt;li&gt;How many false alarms does it produce?&lt;/li&gt;
&lt;li&gt;How does its performance change across agent tasks and tool types?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Experiment B: A fast evaluation judge
&lt;/h3&gt;

&lt;p&gt;Could Jev act as an additional judge for agent outputs?&lt;/p&gt;

&lt;p&gt;For example, use rubric-based scores to evaluate whether an agent's output satisfies specific requirements.&lt;/p&gt;

&lt;p&gt;Compare Jev against existing numerical, rule-based, and LLM-based judges.&lt;/p&gt;

&lt;p&gt;The interesting question isn't just which judge is faster.&lt;/p&gt;

&lt;p&gt;It's whether the scores remain useful and calibrated across different tasks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Experiment C: Replay recorded decisions
&lt;/h3&gt;

&lt;p&gt;Suppose a decision was captured today.&lt;/p&gt;

&lt;p&gt;Three months later, the underlying model has changed.&lt;/p&gt;

&lt;p&gt;Can we replay the &lt;strong&gt;recorded response&lt;/strong&gt; without calling the model again, then separately compare it with a new model evaluation?&lt;/p&gt;

&lt;p&gt;This would help distinguish the historical decision from a new assessment.&lt;/p&gt;

&lt;p&gt;It would not prove that the model itself is deterministic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Experiment D: Multi-agent delegation
&lt;/h3&gt;

&lt;p&gt;Imagine several agents working across machines or organizations.&lt;/p&gt;

&lt;p&gt;One agent delegates a task to another, which then invokes a tool.&lt;/p&gt;

&lt;p&gt;Could we record the delegation chain, assess unusual handoffs, and determine whether authorization remained within the delegated scope?&lt;/p&gt;

&lt;p&gt;I think this is particularly interesting for distributed and multi-agent AI systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. How should we evaluate these ideas?
&lt;/h2&gt;

&lt;p&gt;Before claiming that Jev improves agent safety or reliability, I'd want to run controlled experiments.&lt;/p&gt;

&lt;p&gt;Useful measurements would include:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Measurement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Detection quality&lt;/td&gt;
&lt;td&gt;Precision, recall, PR-AUC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;False alarms&lt;/td&gt;
&lt;td&gt;False-positive rate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Probability quality&lt;/td&gt;
&lt;td&gt;Brier score, calibration error&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance&lt;/td&gt;
&lt;td&gt;p50/p95/p99 latency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Cost per 1,000 evaluations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Robustness&lt;/td&gt;
&lt;td&gt;Behavior under model/version changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Privacy&lt;/td&gt;
&lt;td&gt;Data exposed to the external assessment provider&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reproducibility&lt;/td&gt;
&lt;td&gt;Recorded decision playback and verification&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;An independent Jev evaluation is also relevant:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://arxiv.org/abs/2609.37647" rel="noopener noreferrer"&gt;&lt;strong&gt;Evaluating and Benchmarking the System One Model Jev&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The authors benchmark Jev across 37 datasets and report results that depend on the task and, for some binary decisions, the chosen threshold.&lt;/p&gt;

&lt;p&gt;That's a useful reminder that structured probabilities still require careful evaluation.&lt;/p&gt;

&lt;p&gt;For a research prototype, I'd begin with &lt;strong&gt;offline shadow-mode assessment&lt;/strong&gt; rather than live enforcement.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. A question for the developer community
&lt;/h2&gt;

&lt;p&gt;I think we're reaching a point where recording only an AI agent's final output will not be enough for many applications.&lt;/p&gt;

&lt;p&gt;We need better ways to inspect not just &lt;strong&gt;what an agent did&lt;/strong&gt;, but also the recorded assessments and authorization decisions surrounding its actions.&lt;/p&gt;

&lt;p&gt;I'm particularly interested in feedback on three questions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. What should a verifiable AI decision receipt contain?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Would you preserve complete inputs, hashes, model versions, policy decisions, confidence distributions, or something else?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Where would this be most useful?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI coding agents, autonomous infrastructure operations, financial workflows, multi-agent systems, scientific pipelines, or another area?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Would you start with historical decision analysis or real-time policy assistance?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My current preference is historical analysis and shadow-mode evaluation first, then carefully measured runtime integration.&lt;/p&gt;

&lt;p&gt;I'd be interested to hear how other developers would approach the problem, including reasons this architecture might not work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Projects and research references
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;NovaFabric&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://novafabric.ai/" rel="noopener noreferrer"&gt;https://novafabric.ai/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Open-source repository: &lt;a href="https://github.com/MSKazemi/novafabric" rel="noopener noreferrer"&gt;https://github.com/MSKazemi/novafabric&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Documentation: &lt;a href="https://novafabric.ai/docs/" rel="noopener noreferrer"&gt;https://novafabric.ai/docs/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Archived software release: &lt;a href="https://zenodo.org/records/23652607" rel="noopener noreferrer"&gt;https://zenodo.org/records/23652607&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Research papers&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NovaFabric preprint: &lt;a href="https://arxiv.org/abs/2609.12582" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2609.12582&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Independent Jev benchmarking paper: &lt;a href="https://arxiv.org/abs/2609.37647" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2609.37647&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Jev&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TypeSafe AI introduction: &lt;a href="https://typesafe.ai/blog/introducing-system-one-models-and-jev" rel="noopener noreferrer"&gt;https://typesafe.ai/blog/introducing-system-one-models-and-jev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Documentation: &lt;a href="https://docs.typesafe.ai/introduction" rel="noopener noreferrer"&gt;https://docs.typesafe.ai/introduction&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;About the author&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm Mohsen Seyedkazemi Ardebili, working on AI-agent systems, reproducibility, infrastructure, and verifiable execution evidence.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/MSKazemi" rel="noopener noreferrer"&gt;https://github.com/MSKazemi&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;AI disclosure: This article was drafted with AI assistance. It presents a research proposal, not a completed integration or experimental result.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>architecture</category>
      <category>security</category>
    </item>
    <item>
      <title>The tool that said yes and did nothing: ydotool 0.1.8 exits 0 when it refuses</title>
      <dc:creator>Mohsen Seyedkazemi Ardebili</dc:creator>
      <pubDate>Tue, 27 Oct 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/mskazemi/the-tool-that-said-yes-and-did-nothing-ydotool-018-exits-0-when-it-refuses-2fm</link>
      <guid>https://dev.to/mskazemi/the-tool-that-said-yes-and-did-nothing-ydotool-018-exits-0-when-it-refuses-2fm</guid>
      <description>&lt;p&gt;For about ten and a half hours on 24 September, my dictation app typed nothing at all on Ubuntu under Wayland.&lt;/p&gt;

&lt;p&gt;You held the key, you spoke, the speech was transcribed correctly, and then... nothing landed in the window. No error in the log. The health check printed &lt;code&gt;[OK] Injection: ydotool&lt;/code&gt;. The tests were green.&lt;/p&gt;

&lt;p&gt;This is the story of that bug, because the lesson in it applies to almost every program that shells out to another program.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some context
&lt;/h2&gt;

&lt;p&gt;I work on &lt;a href="https://github.com/MSKazemi/yazses" rel="noopener noreferrer"&gt;YazSes&lt;/a&gt;, an open-source, offline hold-to-talk dictation tool. You hold a key, talk, release, and the text is typed into whatever app has focus. On Wayland you cannot just "send keystrokes" to another window the way you can on X11, so one common route is &lt;a href="https://github.com/ReimuNotMoe/ydotool" rel="noopener noreferrer"&gt;ydotool&lt;/a&gt;, which writes events to a virtual &lt;code&gt;uinput&lt;/code&gt; keyboard.&lt;/p&gt;

&lt;p&gt;The catch: &lt;strong&gt;there are two ydotools.&lt;/strong&gt; Version 1.x and version 0.1.x share a name and a purpose, but they are different command-line tools. And distributions did not all move at the same pace. As of today (I checked Launchpad and sources.debian.org on 2026-10-22):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Distribution&lt;/th&gt;
&lt;th&gt;ydotool version&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ubuntu 22.04, 24.04&lt;/td&gt;
&lt;td&gt;0.1.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debian 11, 12&lt;/td&gt;
&lt;td&gt;0.1.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ubuntu 26.04, Debian testing&lt;/td&gt;
&lt;td&gt;1.0.4&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;YazSes only spoke 1.x.&lt;/p&gt;

&lt;h2&gt;
  
  
  How a correct fix broke things
&lt;/h2&gt;

&lt;p&gt;The bug did not come from a careless change. It came from a &lt;em&gt;correct&lt;/em&gt; one.&lt;/p&gt;

&lt;p&gt;In v2.39.0 I taught the socket discovery to find &lt;code&gt;/tmp/.ydotool_socket&lt;/code&gt;, which is where the 0.1.8 daemon listens. Before that, on Ubuntu, ydotool was simply never selected, and YazSes used another backend that worked fine.&lt;/p&gt;

&lt;p&gt;After the fix, ydotool became reachable. It is first in the backend order. So on Ubuntu it pushed aside a path that worked and took over, speaking 1.x syntax to a 0.1.8 binary.&lt;/p&gt;

&lt;p&gt;Three command lines were now wrong:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ydotool type -d 6 -H 6&lt;/code&gt; was refused. 0.1.x uses &lt;code&gt;--key-delay&lt;/code&gt; and has no hold time.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ydotool key 29:1 47:1 47:0 29:0&lt;/code&gt; (Ctrl+V as raw keycodes): 0.1.x wants symbolic names.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ydotool key 97:0&lt;/code&gt;, a "release any stuck modifier" guard that ran after every dictation, typed the digit &lt;code&gt;9&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why nothing noticed
&lt;/h2&gt;

&lt;p&gt;Here is the part that matters. When 0.1.8 refuses an option, it does this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ydotool type -d 6 -H 6 "hello"
error: unrecognised option '-d'
$ echo $?
0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It prints an error and &lt;strong&gt;exits 0&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;My code ran it with &lt;code&gt;subprocess.run(..., check=True)&lt;/code&gt;. &lt;code&gt;check=True&lt;/code&gt; only raises on a non-zero exit code. So no exception, so the clipboard fallback never ran, so nothing was logged, so the health check (which asked "is ydotool there and does it run?") said OK.&lt;/p&gt;

&lt;p&gt;Every layer was telling the truth about what it was asked. Nobody was asking whether text reached the window.&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%2Fa4nd0v6f64wuqyc9zh1q.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%2Fa4nd0v6f64wuqyc9zh1q.png" alt="Every layer told the truth about what it was asked: exit 0, no exception, no fallback, doctor OK, nothing typed" width="800" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It gets better. 0.1.8's &lt;code&gt;key&lt;/code&gt; command has no error path for unknown names at all. It types the first letter of whatever you gave it. I measured this by grabbing the virtual keyboard device and counting events:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;You send&lt;/th&gt;
&lt;th&gt;0.1.8 types&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Return&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;r&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Escape&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;e&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;space&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;s&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;KEY_BACKSPACE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;k&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;29:1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So "new line" would have typed &lt;code&gt;r&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;Two changes, released in v2.40.0 the same evening.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Stop trusting the exit code.&lt;/strong&gt; The output is classified instead. 0.1.8 writes normal progress messages to stderr ("Key delay was set to 6 milliseconds"), so not every line on stderr is an error, but an explicit error is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_classify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;returncode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;YdotoolCliError&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The exception for this result, or None when it succeeded.

    0.1.8 writes ordinary progress to stderr (&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;notice: Using ydotoold backend&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Key delay was set to 6 milliseconds.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;), so only an explicit error is an error.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;low&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;marker&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;low&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;marker&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;_OPTION_ERROR_MARKERS&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;YdotoolOptionError&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: error:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;low&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;returncode&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&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;YdotoolCliError&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now a refused command raises, and the fallback backend runs like it was always supposed to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Detect the dialect, and only send what it understands.&lt;/strong&gt; YazSes probes which ydotool it is talking to and keeps an allowlist of key names that were &lt;em&gt;measured&lt;/em&gt; to work on 0.1.8. Anything else raises and goes to the fallback, instead of typing a random first letter. &lt;code&gt;yazses doctor&lt;/code&gt; now says "ydotool 0.1.x" or "ydotool 1.x" instead of a bare OK.&lt;/p&gt;

&lt;p&gt;And there is a regression test with an honest name: &lt;code&gt;test_an_option_error_is_a_failure_even_though_ydotool_exits_zero&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I took away
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;An exit code is a claim, not evidence.&lt;/strong&gt; It tells you what the tool &lt;em&gt;thinks&lt;/em&gt; happened. When a subprocess is the last step before something the user sees, read its stderr, and if you possibly can, measure the effect itself. For keyboard injection, that meant grabbing the uinput device and counting events. For your tool it might be "read the file back" or "query the API you just wrote to".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Making something reachable makes it load-bearing.&lt;/strong&gt; My socket fix was right. But it promoted a backend from "never used on Ubuntu" to "first choice on Ubuntu", and I verified the probe, not the backend. If a change widens where code runs, test the code in its new home.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Health check: OK" should name what it checked.&lt;/strong&gt; "ydotool found" and "text arrives in the window" are very different sentences. Only one of them was being said.&lt;/p&gt;

&lt;p&gt;If you maintain anything that wraps &lt;code&gt;ydotool&lt;/code&gt;, &lt;code&gt;xdotool&lt;/code&gt;, &lt;code&gt;wtype&lt;/code&gt; or similar, I'd love to hear how you verify that input actually landed. That is still the hardest part of this whole project.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;YazSes is an offline, open-source (Apache-2.0) dictation tool for Linux, macOS and Windows: &lt;a href="https://github.com/MSKazemi/yazses" rel="noopener noreferrer"&gt;github.com/MSKazemi/yazses&lt;/a&gt;. The full fix is in the &lt;a href="https://github.com/MSKazemi/yazses/blob/main/docs/releases/v2.40.0.md" rel="noopener noreferrer"&gt;v2.40.0 release notes&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I wrote this with help from an AI assistant for drafting and editing; the bug, the fix, the measurements and the opinions are mine, and I checked every fact against the code and release notes.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>python</category>
      <category>debugging</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Anatomy of an offline dictation pipeline: where the time actually goes</title>
      <dc:creator>Mohsen Seyedkazemi Ardebili</dc:creator>
      <pubDate>Thu, 22 Oct 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/mskazemi/anatomy-of-an-offline-dictation-pipeline-where-the-time-actually-goes-18nf</link>
      <guid>https://dev.to/mskazemi/anatomy-of-an-offline-dictation-pipeline-where-the-time-actually-goes-18nf</guid>
      <description>&lt;p&gt;This is the first post in a series about the parts of offline voice dictation that turned out to be harder than they looked. Most of the series is about bugs: the subtle kind, where every check is green and the user still gets the wrong text. But first, a map.&lt;/p&gt;

&lt;p&gt;The project is &lt;a href="https://github.com/MSKazemi/yazses" rel="noopener noreferrer"&gt;YazSes&lt;/a&gt;. You hold a key, speak, let go, and your words are typed into whatever app has focus: editor, terminal, browser, chat. Speech recognition runs on your own CPU. No account, no API key, and no audio leaves the machine.&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%2Fxaip9p9t5x5jiog0ab4o.jpg" 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%2Fxaip9p9t5x5jiog0ab4o.jpg" alt="YazSes recording: the tray icon turns green and rings appear at the cursor" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The nine stages
&lt;/h2&gt;

&lt;p&gt;When you &lt;strong&gt;press&lt;/strong&gt; the key, audio starts going into a buffer in memory. Nothing is written to disk. When you &lt;strong&gt;release&lt;/strong&gt; it, that burst goes through nine stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Hotkey&lt;/strong&gt;: the press and release events (Right Alt on Linux, Right Option on macOS, Right Ctrl on Windows by default).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capture&lt;/strong&gt;: the audio buffer itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Silence gate&lt;/strong&gt;: is this clip loud enough to contain speech at all? If not, drop it. (Post 3 of this series is about what happens when this is set wrong.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Padding&lt;/strong&gt;: a short silent lead-in is added before decoding, so Whisper doesn't clip your first word.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speech-to-text&lt;/strong&gt;: &lt;a href="https://github.com/SYSTRAN/faster-whisper" rel="noopener noreferrer"&gt;faster-whisper&lt;/a&gt; on the CPU, quantised to int8. Default model: &lt;code&gt;base.en&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean-up&lt;/strong&gt;: strips Whisper artefacts like &lt;code&gt;[BLANK_AUDIO]&lt;/code&gt; and stray leading punctuation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Disfluency filter&lt;/strong&gt;: removes fillers, de-duplicates repeated words, and rolls back self-corrections like "no wait".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Command or dictation?&lt;/strong&gt;: "undo that" should press Ctrl+Z, not type the words "undo that". A regex grammar decides, in well under a millisecond.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inject&lt;/strong&gt;: type the text into the focused window, by whatever means the OS allows.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Where the time goes
&lt;/h2&gt;

&lt;p&gt;I assumed I'd have to optimise half of these. I measured instead.&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%2Fzf994k9ntudbc8jvkgnt.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%2Fzf994k9ntudbc8jvkgnt.png" alt="The dictation pipeline, coloured by measured cost" width="800" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On my laptop (an i7-1370P, int8 on CPU, 30 utterances with a median length of 7.3 seconds), speech-to-text with &lt;code&gt;base.en&lt;/code&gt; took a median of &lt;strong&gt;1,561 ms&lt;/strong&gt;. Every other stage &lt;em&gt;combined&lt;/em&gt; had a median of &lt;strong&gt;0.29 ms&lt;/strong&gt;. Speech-to-text is &lt;strong&gt;99.98%&lt;/strong&gt; of the processing time of a burst.&lt;/p&gt;

&lt;p&gt;So the filters, the command grammar and the injector are not worth tuning for speed. The only latency setting that matters is which model you run.&lt;/p&gt;

&lt;p&gt;That made the design question simple. Instead of tuning, offer choices:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Engine&lt;/th&gt;
&lt;th&gt;Word error rate (LibriSpeech test-clean, 200 utterances)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Parakeet TDT 0.6B v2 (optional)&lt;/td&gt;
&lt;td&gt;2.06%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Whisper &lt;code&gt;small.en&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;2.66%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Moonshine base (optional)&lt;/td&gt;
&lt;td&gt;3.17%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Whisper &lt;code&gt;large-v3&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;3.23%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Whisper &lt;code&gt;base.en&lt;/code&gt; (default)&lt;/td&gt;
&lt;td&gt;4.01%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two honest caveats on that table. The confidence intervals overlap, so "Parakeet beats large-v3" is not established by it. And large-v3 loses mostly on &lt;em&gt;insertions&lt;/em&gt; (words that weren't spoken), not on recognising what was said. The full method is in &lt;a href="https://github.com/MSKazemi/yazses/blob/main/docs/benchmarks.md" rel="noopener noreferrer"&gt;docs/benchmarks.md&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The optional engines are not installed by default, because they are big. You turn one on when you want it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yazses features &lt;span class="nb"&gt;enable &lt;/span&gt;stt-parakeet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If an engine you configured is missing, YazSes logs a warning and falls back to faster-whisper instead of refusing to start. (Post 12 is about the day that fallback hid a real bug.)&lt;/p&gt;

&lt;h2&gt;
  
  
  The hard part is the last stage
&lt;/h2&gt;

&lt;p&gt;Speech recognition is the expensive stage. It is not the &lt;em&gt;hard&lt;/em&gt; one. The hard one is stage 9: getting text into someone else's window.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;macOS&lt;/strong&gt;: &lt;code&gt;CGEventKeyboardSetUnicodeString&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows&lt;/strong&gt;: &lt;code&gt;SendInput&lt;/code&gt; with &lt;code&gt;KEYEVENTF_UNICODE&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linux on X11&lt;/strong&gt;: &lt;code&gt;xdotool&lt;/code&gt; works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linux on Wayland&lt;/strong&gt;: there is deliberately no general way for one app to type into another. YazSes probes, in order: &lt;code&gt;ydotool&lt;/code&gt; (a virtual keyboard through &lt;code&gt;uinput&lt;/code&gt;), &lt;code&gt;wtype&lt;/code&gt;, the desktop portal's RemoteDesktop interface, and the clipboard as a last resort.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every one of those has failure modes where the API reports success and the user gets nothing, or gets garbage. Posts 2 and 5 are two of those stories.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why offline at all?
&lt;/h2&gt;

&lt;p&gt;Partly privacy. What you dictate is often the most private text you write: messages, notes, half-formed ideas. But also reliability: it works on a train, it works when a service is down, and it has no per-minute cost. On a modern laptop CPU, small Whisper models are fast enough that you don't need the cloud for this.&lt;/p&gt;

&lt;p&gt;"Nothing leaves the machine" is easy to say. Post 6 is about how the project turns that sentence into a test that fails the build.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rest of the series
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Anatomy of an offline dictation pipeline &lt;em&gt;(this post)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;The tool that said yes and did nothing&lt;/li&gt;
&lt;li&gt;My silence gate was quieter than my room&lt;/li&gt;
&lt;li&gt;Whisper typed its own prompt into my document&lt;/li&gt;
&lt;li&gt;SendInput said success. The app got question marks.&lt;/li&gt;
&lt;li&gt;Turning "nothing leaves the machine" into a test&lt;/li&gt;
&lt;li&gt;Testing a speech app with no microphone, network or model&lt;/li&gt;
&lt;li&gt;A guard is judged by how rarely it fires&lt;/li&gt;
&lt;li&gt;"No wait, I meant...": when self-correction ate my sentences&lt;/li&gt;
&lt;li&gt;Voice commands without an LLM&lt;/li&gt;
&lt;li&gt;Offline meeting transcripts with speaker labels&lt;/li&gt;
&lt;li&gt;The update button that uninstalled a feature&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you want to try it:&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;# Linux&lt;/span&gt;
bash &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/MSKazemi/yazses/main/install.sh&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# macOS (Apple Silicon)&lt;/span&gt;
brew tap MSKazemi/yazses &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; brew trust MSKazemi/yazses &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; yazses
&lt;span class="c"&gt;# Windows&lt;/span&gt;
winget &lt;span class="nb"&gt;install &lt;/span&gt;MSKazemi.YazSes
&lt;span class="c"&gt;# Any OS with Python 3.11+&lt;/span&gt;
pipx &lt;span class="nb"&gt;install &lt;/span&gt;yazses
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Linux is the most tested platform. macOS and Windows are labelled preview, and reports from real machines help more than anything else.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;YazSes is open source under Apache-2.0: &lt;a href="https://github.com/MSKazemi/yazses" rel="noopener noreferrer"&gt;github.com/MSKazemi/yazses&lt;/a&gt;. The architecture reference, with the measurements above: &lt;a href="https://github.com/MSKazemi/yazses/blob/main/docs/architecture.md" rel="noopener noreferrer"&gt;docs/architecture.md&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I wrote this with help from an AI assistant for drafting and editing; the project, the measurements and the opinions are mine, and I checked every number against the repository.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>linux</category>
      <category>opensource</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Find where two AI agent runs diverged</title>
      <dc:creator>Mohsen Seyedkazemi Ardebili</dc:creator>
      <pubDate>Sat, 10 Oct 2026 09:41:50 +0000</pubDate>
      <link>https://dev.to/mskazemi/find-where-two-ai-agent-runs-diverged-55fp</link>
      <guid>https://dev.to/mskazemi/find-where-two-ai-agent-runs-diverged-55fp</guid>
      <description>&lt;p&gt;Your agent gave one answer yesterday and a different one today. Same code, same input file, a different decision. Before you can fix anything you need to know where the two runs parted ways. Did the prompt change, did the model reply change, or did something after the model call change?&lt;/p&gt;

&lt;p&gt;This tutorial is for engineers who run agents in Python and want to answer that from two recorded runs instead of from memory. You will capture two runs as &lt;a href="https://novafabric.ai/docs/architecture/run-capsule/" rel="noopener noreferrer"&gt;Run Capsules&lt;/a&gt;, compare them with &lt;code&gt;nova diff&lt;/code&gt;, read which model call changed, and turn the same comparison into a CI gate. Every command and output below was captured from the released &lt;code&gt;novafabric 0.104.0&lt;/code&gt;. Long paths and skipped lines are marked with &lt;code&gt;…&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What you need
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.12 or newer (the package declares &lt;code&gt;Requires-Python: &amp;gt;=3.12&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pip install novafabric openai&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;examples/blackbox_demo/&lt;/code&gt; directory from the public &lt;a href="https://github.com/MSKazemi/novafabric" rel="noopener noreferrer"&gt;MSKazemi/novafabric&lt;/a&gt; repository, copied into an empty working directory. Run every command from that working directory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No API key and no hosted model are involved. The example ships a small OpenAI-compatible mock server on &lt;code&gt;127.0.0.1:9099&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The scenario
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;agent.py&lt;/code&gt; reads a payment-service config, asks a model for one recommended change, and writes the answer to &lt;code&gt;outputs/decision.json&lt;/code&gt;. The mock server answers in one of two ways, chosen by a request header the agent sends: a risky answer (disable rate limiting) or a safe one (reduce &lt;code&gt;max_connections&lt;/code&gt;). That gives you two runs whose behaviour differs for a known reason, so you can check that the diff points at the right place.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: start the mock model
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python blackbox_demo/mock_llm_server.py &amp;amp;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sk-demo-no-key-needed
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENAI_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://127.0.0.1:9099
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;NOVAFABRIC_SUGGEST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: capture the first run
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;nova capture&lt;/code&gt; wraps the command with no code changes. Model calls from Python workloads are recorded automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ nova capture -- python blackbox_demo/agent.py --mode bad
✓ Capsule written: …/.novafabric/capsules/01M4FBTQ7FVQEZBWQGFD2RVTW2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the capsule path in a variable (your run IDs will differ):&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="nv"&gt;BAD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.novafabric/capsules/01M4FBTQ7FVQEZBWQGFD2RVTW2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: capture the second run
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ nova capture -- python blackbox_demo/agent.py --mode fixed
✓ Capsule written: …/.novafabric/capsules/01M4FBVEZDG7XVBYJBB9WD6Q8D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: diff the two runs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ nova diff "$BAD" "$FIXED"
  changed=3  added=0  removed=0

Model calls:
  ~ call at span 5fef2505ba0bd78e
Outputs:
  ~ outputs/decision.json
  ~ outputs/stdout.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three items changed (&lt;code&gt;~&lt;/code&gt;): one model call and two output files. Nothing was added or removed. For the machine-readable record, add &lt;code&gt;--output-format json&lt;/code&gt;; the model-call pair reports &lt;code&gt;request_changed: false&lt;/code&gt; and &lt;code&gt;response_changed: true&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to read it
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;aligned: 1&lt;/code&gt;&lt;/strong&gt;: the diff paired the one model call in each run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;request_changed: false&lt;/code&gt;, &lt;code&gt;response_changed: true&lt;/code&gt;&lt;/strong&gt;: the agent sent the same request both times and got a different reply. The runs diverged at the model's answer, not in your prompt-building code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;environment.changes: []&lt;/code&gt;&lt;/strong&gt;: no recorded environment difference to chase.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;outputs&lt;/code&gt;&lt;/strong&gt;: both files changed downstream of that reply.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 5: use it as a CI gate
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;--assert-no-regressions&lt;/code&gt; makes &lt;code&gt;nova diff&lt;/code&gt; exit 1 when it finds &lt;strong&gt;any&lt;/strong&gt; structural change between the two runs, including added or removed calls. Comparing a run with itself exits 0. On GitHub Actions, &lt;code&gt;--output-format github-annotation&lt;/code&gt; prints one annotation per change.&lt;/p&gt;

&lt;p&gt;Read the gate for what it is: it answers "did anything change?", not "did it get worse?". A safer answer fails it just as a riskier one does, as it did here.&lt;/p&gt;

&lt;h3&gt;
  
  
  What this does not do
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It does not judge the change.&lt;/strong&gt; The diff is structural: it tells you which call and which files differ, not whether the new behaviour is better.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Any difference counts.&lt;/strong&gt; Against a live model, two runs of the same prompt can return different text, and the gate fails on that too.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic model-call capture is for Python workloads.&lt;/strong&gt; Other clients go through &lt;code&gt;nova api-proxy&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sealing is opt-in&lt;/strong&gt; and was not used here. The capsule format is pre-1.0.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The full version, with every command output, the replay section and the CI examples, is on the original page: &lt;a href="https://novafabric.ai/blog/find-where-two-agent-runs-diverged/" rel="noopener noreferrer"&gt;https://novafabric.ai/blog/find-where-two-agent-runs-diverged/&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://novafabric.ai/blog/find-where-two-agent-runs-diverged/" rel="noopener noreferrer"&gt;novafabric.ai&lt;/a&gt;. NovaFabric is open source (Apache-2.0) and pre-1.0: &lt;a href="https://github.com/MSKazemi/novafabric" rel="noopener noreferrer"&gt;github.com/MSKazemi/novafabric&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>debugging</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I got tired of typing, so I built a free, offline voice-typing tool (open source, contributors wanted)</title>
      <dc:creator>Mohsen Seyedkazemi Ardebili</dc:creator>
      <pubDate>Thu, 08 Oct 2026 15:04:54 +0000</pubDate>
      <link>https://dev.to/mskazemi/i-got-tired-of-typing-so-i-built-a-free-offline-voice-typing-tool-open-source-contributors-2fgn</link>
      <guid>https://dev.to/mskazemi/i-got-tired-of-typing-so-i-built-a-free-offline-voice-typing-tool-open-source-contributors-2fgn</guid>
      <description>&lt;p&gt;I built &lt;strong&gt;YazSes&lt;/strong&gt;: hold a key, speak, release, and your words are typed into whatever app has focus. It runs speech-to-text locally (faster-whisper on CPU), so nothing leaves your machine: no cloud, no account, no API key.&lt;/p&gt;


&lt;div&gt;
    &lt;iframe src="https://www.youtube.com/embed/u4m5Cvexjq0" width="315" height="560"&gt;
    &lt;/iframe&gt;
  &lt;/div&gt;


&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Linux&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;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/MSKazemi/yazses/main/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Snap&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;&lt;span class="nb"&gt;sudo &lt;/span&gt;snap &lt;span class="nb"&gt;install &lt;/span&gt;yazses
&lt;span class="nb"&gt;sudo &lt;/span&gt;snap connect yazses:audio-record   &lt;span class="c"&gt;# microphone&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;snap connect yazses:raw-input      &lt;span class="c"&gt;# hold-to-talk key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;macOS (Apple Silicon)&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;brew tap MSKazemi/yazses &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; brew trust MSKazemi/yazses &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; yazses
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Windows&lt;/strong&gt;&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;winget&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MSKazemi.YazSes&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;Any OS with Python 3.11+&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;pipx &lt;span class="nb"&gt;install &lt;/span&gt;yazses
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yazses quickstart   &lt;span class="c"&gt;# 3 steps tailored to your machine, read-only&lt;/span&gt;
yazses doctor       &lt;span class="c"&gt;# verify microphone, hotkey and text injection&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why offline?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Your voice never leaves the machine.&lt;/li&gt;
&lt;li&gt;No subscription, no API key, works without internet.&lt;/li&gt;
&lt;li&gt;It also transcribes recordings and captures meetings with speaker labels, locally.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GitHub (stars welcome): &lt;a href="https://github.com/MSKazemi/yazses" rel="noopener noreferrer"&gt;https://github.com/MSKazemi/yazses&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Install guides: &lt;a href="https://mskazemi.com/yazses/install-linux.html" rel="noopener noreferrer"&gt;Linux&lt;/a&gt; · &lt;a href="https://mskazemi.com/yazses/macos-install.html" rel="noopener noreferrer"&gt;macOS&lt;/a&gt; · &lt;a href="https://mskazemi.com/yazses/windows-install.html" rel="noopener noreferrer"&gt;Windows&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://mskazemi.com/yazses" rel="noopener noreferrer"&gt;https://mskazemi.com/yazses&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which OS are you on? Tell me what breaks and I'll fix it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Want to contribute? Here is where you can help in 15 minutes
&lt;/h2&gt;

&lt;p&gt;You do not need to be a speech-recognition expert. Real, open tasks right now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🌍 &lt;strong&gt;Native speaker of Persian, Urdu, Thai, Vietnamese, Telugu, Ukrainian, Traditional Chinese...?&lt;/strong&gt; Several translations of the docs have never been read by a native speaker. Fixing a sentence is a valid first PR.&lt;/li&gt;
&lt;li&gt;🧪 &lt;strong&gt;Pick a &lt;code&gt;good first issue&lt;/code&gt;&lt;/strong&gt;: &lt;a href="https://github.com/MSKazemi/yazses/labels/good%20first%20issue" rel="noopener noreferrer"&gt;https://github.com/MSKazemi/yazses/labels/good%20first%20issue&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🐧 &lt;strong&gt;Test it on your distro / desktop&lt;/strong&gt; (Wayland, X11, Windows, macOS) and open an issue with the output of &lt;code&gt;yazses doctor&lt;/code&gt;. Bug reports from real machines are gold.&lt;/li&gt;
&lt;li&gt;📖 &lt;strong&gt;Read the guide&lt;/strong&gt;: &lt;a href="https://github.com/MSKazemi/yazses/blob/main/.github/CONTRIBUTING.md" rel="noopener noreferrer"&gt;https://github.com/MSKazemi/yazses/blob/main/.github/CONTRIBUTING.md&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The test suite runs offline (no microphone, no model download). It is about 30,000 tests now and takes a few minutes on a laptop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv &lt;span class="nb"&gt;sync
&lt;/span&gt;uv run python &lt;span class="nt"&gt;-m&lt;/span&gt; pytest tests/ &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Try it, then tell me
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;⭐ &lt;strong&gt;Star the repo&lt;/strong&gt; so others can find it: &lt;a href="https://github.com/MSKazemi/yazses" rel="noopener noreferrer"&gt;https://github.com/MSKazemi/yazses&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;⬇️ &lt;strong&gt;Install it&lt;/strong&gt; with the one-liner above and run &lt;code&gt;yazses doctor&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;💬 &lt;strong&gt;Comment below&lt;/strong&gt; with your OS and what happened. I reply to every comment.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>opensource</category>
      <category>productivity</category>
      <category>linux</category>
      <category>showdev</category>
    </item>
    <item>
      <title>AI Agents Need More Than Logs: Why I Built NovaFabric</title>
      <dc:creator>Mohsen Seyedkazemi Ardebili</dc:creator>
      <pubDate>Wed, 07 Oct 2026 10:10:58 +0000</pubDate>
      <link>https://dev.to/mskazemi/ai-agents-need-more-than-logs-why-i-built-novafabric-1emd</link>
      <guid>https://dev.to/mskazemi/ai-agents-need-more-than-logs-why-i-built-novafabric-1emd</guid>
      <description>&lt;p&gt;AI agents are becoming more powerful.&lt;/p&gt;

&lt;p&gt;They can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;modify code&lt;/li&gt;
&lt;li&gt;call APIs&lt;/li&gt;
&lt;li&gt;execute shell commands&lt;/li&gt;
&lt;li&gt;change cloud infrastructure&lt;/li&gt;
&lt;li&gt;read and write files&lt;/li&gt;
&lt;li&gt;interact with other agents&lt;/li&gt;
&lt;li&gt;make decisions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But there is a simple question that is still surprisingly hard to answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;After an AI agent finishes its work, can we prove what it actually did?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Usually, the answer is: &lt;strong&gt;not completely&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That is the problem behind &lt;strong&gt;NovaFabric&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logs are useful, but logs are not evidence
&lt;/h2&gt;

&lt;p&gt;Today we have many good observability tools. They can show things like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Agent started
↓
LLM called
↓
Tool called
↓
API called
↓
File modified
↓
Agent finished
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is useful for debugging. But imagine that six months later something goes wrong. An auditor, security engineer, researcher, or developer may ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which model was used?&lt;/li&gt;
&lt;li&gt;What prompt did it receive?&lt;/li&gt;
&lt;li&gt;What did the model return?&lt;/li&gt;
&lt;li&gt;Which tools did the agent call, and with what arguments?&lt;/li&gt;
&lt;li&gt;Which files changed?&lt;/li&gt;
&lt;li&gt;Which network services were contacted?&lt;/li&gt;
&lt;li&gt;What software versions were installed?&lt;/li&gt;
&lt;li&gt;Were secrets captured?&lt;/li&gt;
&lt;li&gt;Was the record changed after the run?&lt;/li&gt;
&lt;li&gt;Can we reproduce or inspect the execution again?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A traditional trace does not necessarily answer all of these questions. And more importantly, a normal database record can potentially be changed later.&lt;/p&gt;

&lt;p&gt;That is where the idea of &lt;strong&gt;execution evidence&lt;/strong&gt; becomes useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Think of an AI run as an artifact
&lt;/h2&gt;

&lt;p&gt;NovaFabric takes a different approach. Instead of thinking:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI Agent → Logs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;it thinks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI Agent
   ↓
Execution
   ↓
Portable Evidence
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NovaFabric captures a run into something called a &lt;strong&gt;Run Capsule&lt;/strong&gt;: a structured folder describing one execution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;capsule/
├── capsule.yaml
├── trace.jsonl
├── model-calls.jsonl
├── tool-calls.jsonl
├── env.lock
├── redaction-proof.json
├── replay.yaml
└── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The idea is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The execution should leave behind something you can keep, inspect, compare, replay, sign, and share.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The research paper models the capsule using &lt;strong&gt;15 entity types&lt;/strong&gt;, including model calls, tool calls, file events, network calls, human approvals, environment information, lineage, redaction records, replay information, and tamper-evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Five verbs explain most of NovaFabric
&lt;/h2&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%2F0vw8mdo8d08fqtr8mgmx.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%2F0vw8mdo8d08fqtr8mgmx.png" alt="NovaFabric: from AI agent runs to verifiable evidence - capture, seal, replay, diff, audit" width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Capture → Seal → Replay → Diff → Audit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1. Capture
&lt;/h3&gt;

&lt;p&gt;First, capture what happened during the execution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nova capture python my_agent.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NovaFabric can wrap a command without requiring the application itself to be rewritten. It aims to capture model calls, tool calls, files, network activity, environment, inputs and outputs. The result is a portable &lt;strong&gt;Run Capsule&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Seal
&lt;/h3&gt;

&lt;p&gt;Capturing information is not enough. If somebody modifies the record afterward, we want that modification to be detectable.&lt;/p&gt;

&lt;p&gt;NovaFabric combines established technologies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DSSE&lt;/strong&gt; for signing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RFC 3161&lt;/strong&gt; trusted timestamps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Merkle logs&lt;/strong&gt; for append-only history&lt;/li&gt;
&lt;li&gt;cryptographic redaction records&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important point: NovaFabric is &lt;strong&gt;not inventing new cryptography&lt;/strong&gt;. It combines existing standards into one evidence system for AI-agent executions. That integration, rather than a new signing algorithm, is the paper's main contribution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before sealing:  "This file says the agent did X."

After sealing:   "This file says the agent did X,
                  and we can detect if somebody changes it later."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is an important limitation here. &lt;strong&gt;Tamper-evident does not mean tamper-proof.&lt;/strong&gt; NovaFabric can detect later modification of properly sealed evidence. It cannot prove that a trusted signing party told the truth when the evidence was originally created. The paper explicitly defines this trust boundary.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Replay
&lt;/h3&gt;

&lt;p&gt;AI systems have another problem: reproducibility. Run the same prompt tomorrow and you may not get the same answer. So NovaFabric does not pretend every AI execution can be reproduced bit-for-bit. Instead, it defines four replay modes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Forensic replay&lt;/strong&gt; - execute nothing; reconstruct and inspect what happened (incident investigation, security review, audit, debugging).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mocked replay&lt;/strong&gt; - use previously recorded model responses instead of calling the model again, to test the surrounding application logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exact replay&lt;/strong&gt; - try to recreate the same environment, model, data, and execution, when deterministic conditions exist.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic replay&lt;/strong&gt; - run against another model and compare the &lt;em&gt;meaning&lt;/em&gt; of the new output with the original, e.g. when upgrading from Model v1 to v2.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The paper is deliberately careful here: only the mocked mode is deterministic by construction with respect to model inputs, while forensic mode executes nothing.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Diff
&lt;/h3&gt;

&lt;p&gt;Run B produces a bad result. What changed? Maybe the model, the prompt, a dependency, a tool response, the environment, or the output. NovaFabric provides a structural diff between capsules, comparing the structure of runs instead of giant text logs.&lt;/p&gt;

&lt;p&gt;In the paper's controlled experiment, structural diff correctly localized all &lt;strong&gt;140 injected mutations&lt;/strong&gt; across seven tested mutation classes. That could make questions like this easier to investigate:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It worked yesterday. Why did the agent fail today?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  5. Audit
&lt;/h3&gt;

&lt;p&gt;Finally, the evidence can be packaged into an &lt;strong&gt;Evidence Bundle&lt;/strong&gt;. Another person should not have to trust your database; you can hand them a portable artifact containing the evidence and the cryptographic information needed to verify it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Agent execution → Run Capsule → Cryptographic seal → Evidence Bundle → Auditor / Researcher / Security Team
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The paper specifies an offline verification path using standard technologies rather than requiring the verifier to trust a NovaFabric server. However, it also states that this independent-verifier interoperability path had &lt;strong&gt;not yet been exercised by an independently implemented verifier&lt;/strong&gt; at publication time. That distinction matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not just use OpenTelemetry?
&lt;/h2&gt;

&lt;p&gt;I like OpenTelemetry, and NovaFabric uses OpenTelemetry concepts. But the problems are different:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Observability&lt;/th&gt;
&lt;th&gt;Execution evidence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;What is happening?&lt;/td&gt;
&lt;td&gt;What happened?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Find errors&lt;/td&gt;
&lt;td&gt;Reconstruct the execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metrics and traces&lt;/td&gt;
&lt;td&gt;Portable run artifact&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operational debugging&lt;/td&gt;
&lt;td&gt;Audit and forensics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Usually mutable storage&lt;/td&gt;
&lt;td&gt;Tamper-evident evidence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Live system&lt;/td&gt;
&lt;td&gt;Historical execution&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;NovaFabric is &lt;strong&gt;not intended to replace Prometheus, OpenTelemetry, Langfuse, LangSmith, or similar systems&lt;/strong&gt;. It focuses on another question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can a past execution be replayed, compared, and proven?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What about secrets?
&lt;/h2&gt;

&lt;p&gt;Capturing everything creates another problem: an agent may see API keys, tokens, passwords, or credentials, and you do not want them inside a bundle sent to another company or auditor. NovaFabric includes secret redaction and a redaction attestation.&lt;/p&gt;

&lt;p&gt;But a redaction attestation means "the scanner says it detected and removed this secret." It does &lt;strong&gt;not&lt;/strong&gt; prove that an unknown secret does not exist somewhere else in the capsule. The paper makes this explicit: completeness depends on the scanner, its rules, and the locations it checks. Being clear about these boundaries is especially important for security tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  What did the research evaluation find?
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Model calls captured:                 100% in tested scenarios
MCP tool calls captured:              100% in tested scenarios
Tested tampering classes detected:    3 / 3
Structural mutations localized:       140 / 140
Redaction (14 credential types):      14 / 14 removed
Provenance graph, 10M-edge blast-radius p99:   45.5 ms
Provenance graph, 100M-edge single-client p99: 167.9 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But some results exposed weaknesses too. Mocked replay avoided live model calls in &lt;strong&gt;10/10&lt;/strong&gt; tested scenarios, but only &lt;strong&gt;2/10&lt;/strong&gt; tool-using workloads completed, because tool responses were not yet being substituted in the evaluated version. That was one of the most useful outcomes of the research: the evaluation did not only produce good benchmark numbers, it found real defects.&lt;/p&gt;

&lt;h2&gt;
  
  
  An unexpected lesson
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"The pipeline ran successfully" is not the same as "the evidence is correct."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Several problems were discovered because the tests checked what &lt;em&gt;should have been recorded&lt;/em&gt;, rather than simply checking whether the program exited successfully. At one point some evidence streams were missing even though capture appeared to work. In another experiment, an ingest service returned successful HTTP responses even though its configured metadata database had no tables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Did it run?" is a weak test.
"Can we prove the expected things actually happened?" is a much stronger one.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  NovaFabric today
&lt;/h2&gt;

&lt;p&gt;NovaFabric is open source and self-hosted. The project has moved beyond the exact version evaluated in the paper, so the paper should be read as a &lt;strong&gt;research snapshot&lt;/strong&gt;, not a complete description of today's repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;novafabric

nova capture python my_agent.py
nova validate &amp;lt;capsule&amp;gt;
nova replay &amp;lt;capsule&amp;gt; &lt;span class="nt"&gt;--mode&lt;/span&gt; forensic
nova diff &amp;lt;capsule-a&amp;gt; &amp;lt;capsule-b&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project is still &lt;strong&gt;beta&lt;/strong&gt;. Some local functionality is more mature, while server, cluster-scale, and some at-scale components remain experimental. I do not want to describe experimental infrastructure as production-proven.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bigger idea
&lt;/h2&gt;

&lt;p&gt;As AI agents move from chat interfaces into systems that actually &lt;strong&gt;do things&lt;/strong&gt; - changing production infrastructure, approving financial operations, modifying software, moving important data, operating HPC systems, coordinating other agents - observability alone will not be enough. Eventually someone will ask: &lt;em&gt;What exactly happened? Can you prove it? Can you reproduce it?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That is the space NovaFabric is exploring. Not another agent framework, not another tracing dashboard, but infrastructure for turning an execution into something we can &lt;strong&gt;capture → seal → replay → compare → audit&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;NovaFabric is Apache-2.0 and open source:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/MSKazemi/novafabric" rel="noopener noreferrer"&gt;https://github.com/MSKazemi/novafabric&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://novafabric.ai" rel="noopener noreferrer"&gt;https://novafabric.ai&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Research paper:&lt;/strong&gt; &lt;a href="https://arxiv.org/abs/2609.12582" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2609.12582&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you work on AI agents, MLOps, HPC, security, observability, provenance, or reproducibility, I would love your feedback. Especially this question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;When your AI agent makes an important decision today, what evidence will you still have six months from now?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>security</category>
      <category>devops</category>
    </item>
    <item>
      <title>YazSes</title>
      <dc:creator>Mohsen Seyedkazemi Ardebili</dc:creator>
      <pubDate>Wed, 23 Sep 2026 11:32:26 +0000</pubDate>
      <link>https://dev.to/mskazemi/yazses-3950</link>
      <guid>https://dev.to/mskazemi/yazses-3950</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%2Fmvy6mtv9aqtk9uhr1hi2.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%2Fmvy6mtv9aqtk9uhr1hi2.png" alt=" " width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Help Us Build an Open, Offline Hands-Free Desktop</title>
      <dc:creator>Mohsen Seyedkazemi Ardebili</dc:creator>
      <pubDate>Wed, 23 Sep 2026 11:31:04 +0000</pubDate>
      <link>https://dev.to/mskazemi/help-us-build-an-open-offline-hands-free-desktop-13ip</link>
      <guid>https://dev.to/mskazemi/help-us-build-an-open-offline-hands-free-desktop-13ip</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Update (2026-10-25):&lt;/strong&gt; PR #413 was merged on 25 September 2026 as a planning and evaluation document, not a runtime release. Issue #524 (the ADR numbering collision) is now closed. PR #541 was closed as a duplicate, because the same status correction had already landed on &lt;code&gt;main&lt;/code&gt;. Issue #102, the hands-free epic, is still open and is still the place to help.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;YazSes started with a simple idea:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hold a key, speak, and have your words typed into almost any desktop application — locally, without sending your audio to a cloud service by default.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;GitHub:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/MSKazemi/yazses" rel="noopener noreferrer"&gt;https://github.com/MSKazemi/yazses&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But voice is only one part of hands-free computer interaction.&lt;/p&gt;

&lt;p&gt;What if the same open-source project could eventually let you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;look toward a window and dictate into it,&lt;/li&gt;
&lt;li&gt;look roughly at something and say &lt;strong&gt;“close this”&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;move a pointer using your head,&lt;/li&gt;
&lt;li&gt;use an intentional facial gesture as a switch,&lt;/li&gt;
&lt;li&gt;use dwell when another physical switch is unavailable,&lt;/li&gt;
&lt;li&gt;combine gaze + speech + head movement + switch input,&lt;/li&gt;
&lt;li&gt;and safely fall back when the camera is uncertain?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the direction of the &lt;strong&gt;YazSes eye / camera control programme&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And this is an invitation to help us build it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6iid7zs9hc8rc159dh1k.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6iid7zs9hc8rc159dh1k.gif" alt="YazSes demo" width="600" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  First: this is not “perfect eye tracking with a laptop webcam”
&lt;/h2&gt;

&lt;p&gt;This distinction is important.&lt;/p&gt;

&lt;p&gt;A normal laptop webcam is &lt;strong&gt;not&lt;/strong&gt; the same thing as a dedicated infrared eye tracker.&lt;/p&gt;

&lt;p&gt;We do not want to pretend otherwise.&lt;/p&gt;

&lt;p&gt;Commodity-camera gaze may be useful for answering a coarse question such as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which window or region is the user looking toward?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It should not automatically be treated as a precision mouse pointer capable of selecting a tiny icon or placing a text caret.&lt;/p&gt;

&lt;p&gt;Our current design gives different modalities different jobs.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Input that may fit it best&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Generate text&lt;/td&gt;
&lt;td&gt;Speech&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Choose a pane/window&lt;/td&gt;
&lt;td&gt;Coarse gaze&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resolve “this” or “that window”&lt;/td&gt;
&lt;td&gt;Gaze + speech&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Move a pointer continuously&lt;/td&gt;
&lt;td&gt;Head pose&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commit an action&lt;/td&gt;
&lt;td&gt;Face switch, voice, dwell, keyboard, EMG, etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Precision gaze pointing&lt;/td&gt;
&lt;td&gt;Future dedicated eye-tracker backend&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recover from uncertainty&lt;/td&gt;
&lt;td&gt;Current focus, voice controls, keyboard/switch, or explicit abstention&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One principle from the design summarizes this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Perception proposes; an intent-bearing action commits.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Your eyes can help say &lt;strong&gt;where&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Your voice or another deliberate action can say &lt;strong&gt;what to do&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That matters because our eyes are always looking at something.&lt;/p&gt;

&lt;p&gt;Looking at a button should not automatically mean clicking it.&lt;/p&gt;




&lt;h1&gt;
  
  
  Some gaze functionality already exists
&lt;/h1&gt;

&lt;p&gt;This is not just a concept document.&lt;/p&gt;

&lt;p&gt;YazSes already contains working gaze-related code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Glance-Type gaze routing
&lt;/h2&gt;

&lt;p&gt;The implemented gaze path can sample webcam gaze, use a calibration map, resolve a target window, and route the next dictation toward it.&lt;/p&gt;

&lt;p&gt;When confidence is insufficient, it falls back rather than blindly choosing another window.&lt;/p&gt;

&lt;p&gt;The relevant runtime code already exists in the repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/yazses/gaze/mediapipe_backend.py
src/yazses/gaze/calibrate.py
src/yazses/gaze/targeter.py
src/yazses/gaze/route.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look-to-window routing is currently associated particularly with the X11 desktop path, and cross-platform semantics are one of the things we still need to improve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gaze + speech deixis
&lt;/h2&gt;

&lt;p&gt;YazSes also has a particularly interesting interaction:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;look + speak&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“focus that window”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;or:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“close this”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The speech provides the operation while the gaze snapshot provides context for what &lt;strong&gt;this&lt;/strong&gt; or &lt;strong&gt;that&lt;/strong&gt; refers to.&lt;/p&gt;

&lt;p&gt;Destructive gaze-routed actions can go through confirmation instead of being executed blindly.&lt;/p&gt;

&lt;p&gt;The important part is that gaze provides context rather than becoming an automatic click mechanism.&lt;/p&gt;




&lt;h1&gt;
  
  
  Other pieces already exist — but are not complete runtime features
&lt;/h1&gt;

&lt;p&gt;This is where the project becomes interesting for contributors.&lt;/p&gt;

&lt;p&gt;Several useful cores already exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implicit gaze calibration
&lt;/h2&gt;

&lt;p&gt;There is already a pure implicit-calibration implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/yazses/gaze/implicit.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It can update a calibration model from &lt;code&gt;(gaze, click)&lt;/code&gt; observations using recursive least squares.&lt;/p&gt;

&lt;p&gt;It also contains important safeguards:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reject low-confidence samples,&lt;/li&gt;
&lt;li&gt;reject extreme residuals,&lt;/li&gt;
&lt;li&gt;use a forgetting factor,&lt;/li&gt;
&lt;li&gt;compare a candidate calibration against a baseline,&lt;/li&gt;
&lt;li&gt;keep the original calibration unless the candidate performs better on held-out samples.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the pure estimator is not the same as a complete feature.&lt;/p&gt;

&lt;p&gt;The runtime still needs proper opt-in click observation, sample association, persistence and rollback work.&lt;/p&gt;




&lt;h1&gt;
  
  
  Head-Pointer is another example
&lt;/h1&gt;

&lt;p&gt;YazSes already contains the pure head-pose-to-pointer core:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/yazses/headpointer/pointer.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;yaw/pitch → cursor delta mapping,&lt;/li&gt;
&lt;li&gt;a configurable dead zone,&lt;/li&gt;
&lt;li&gt;and a dwell-click state machine.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;head movement
      ↓
  yaw / pitch
      ↓
dead zone + mapping
      ↓
pointer movement
      ↓
optional dwell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But this is &lt;strong&gt;not currently a complete Head-Pointer desktop feature&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The missing engineering includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;shared camera/head-pose input,&lt;/li&gt;
&lt;li&gt;a platform-neutral pointer interface,&lt;/li&gt;
&lt;li&gt;X11 output,&lt;/li&gt;
&lt;li&gt;Wayland output,&lt;/li&gt;
&lt;li&gt;macOS output,&lt;/li&gt;
&lt;li&gt;Windows output,&lt;/li&gt;
&lt;li&gt;pause/resume,&lt;/li&gt;
&lt;li&gt;recentering,&lt;/li&gt;
&lt;li&gt;signal-loss handling,&lt;/li&gt;
&lt;li&gt;dwell feedback,&lt;/li&gt;
&lt;li&gt;and reliable emergency stopping.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A continuous accessibility control without a dependable stop path is not something we want to ship.&lt;/p&gt;




&lt;h1&gt;
  
  
  Face gestures should become switches — not direct actions
&lt;/h1&gt;

&lt;p&gt;Another planned capability is deliberate facial input.&lt;/p&gt;

&lt;p&gt;Imagine intentionally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;opening your mouth,&lt;/li&gt;
&lt;li&gt;raising your eyebrows,&lt;/li&gt;
&lt;li&gt;making another calibrated expression,&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and having that produce an abstract &lt;strong&gt;switch event&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eyebrow movement → directly click something
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;camera
   ↓
face/blendshape signal
   ↓
gesture detector
   ↓
abstract switch event
   ↓
user-configured action
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That difference matters.&lt;/p&gt;

&lt;p&gt;The camera detector should recognize intent.&lt;/p&gt;

&lt;p&gt;It should not contain desktop-action policy.&lt;/p&gt;

&lt;p&gt;The eventual switch could be mapped to things such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;start/stop dictation,&lt;/li&gt;
&lt;li&gt;click,&lt;/li&gt;
&lt;li&gt;confirm,&lt;/li&gt;
&lt;li&gt;cancel,&lt;/li&gt;
&lt;li&gt;pause the pointer,&lt;/li&gt;
&lt;li&gt;participate in a gesture chord.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;YazSes already has an input-agnostic Gesture Chord core:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/yazses/gesture/chords.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the &lt;strong&gt;Face-Gesture Switch itself does not currently have a runtime detector/adapter implementation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That distinction was important enough that we are currently correcting stale documentation which previously implied otherwise:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/MSKazemi/yazses/pull/541" rel="noopener noreferrer"&gt;https://github.com/MSKazemi/yazses/pull/541&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  One camera, not three competing camera loops
&lt;/h1&gt;

&lt;p&gt;If gaze, head pose and facial switches all use the same webcam, each feature should not independently open the camera and run its own face model.&lt;/p&gt;

&lt;p&gt;The architecture under review proposes a shared perception source:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                         camera
                            |
                            v
                  +-------------------+
                  | Shared Perception |
                  | one camera owner  |
                  | one face model    |
                  +---------+---------+
                            |
          +-----------------+-----------------+
          |                 |                 |
          v                 v                 v
      GazeSignal       HeadPoseSignal      FaceSignal
          |                 |                 |
          v                 v                 v
      calibration       Head-Pointer      Face Switch
          |                 |                 |
          +-----------------+-----------------+
                            |
                     safety / feedback
                            |
                         YazSes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Raw frames stay inside the perception layer.&lt;/p&gt;

&lt;p&gt;Consumers should receive small derived signals.&lt;/p&gt;

&lt;p&gt;That has several advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one webcam owner,&lt;/li&gt;
&lt;li&gt;less duplicated computation,&lt;/li&gt;
&lt;li&gt;fewer camera-contention bugs,&lt;/li&gt;
&lt;li&gt;easier testing,&lt;/li&gt;
&lt;li&gt;clearer privacy boundaries,&lt;/li&gt;
&lt;li&gt;easier failure handling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also means most of the system can be tested with deterministic numeric inputs rather than requiring a physical webcam in CI.&lt;/p&gt;




&lt;h1&gt;
  
  
  Privacy needs to be part of the architecture
&lt;/h1&gt;

&lt;p&gt;Camera-based accessibility software deserves careful handling of data.&lt;/p&gt;

&lt;p&gt;For this programme, the intended boundary is straightforward:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Camera processing stays local.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Raw webcam frames should not become part of normal logs, evaluation exports, issue reports or network requests.&lt;/p&gt;

&lt;p&gt;For engineering evaluation, we care about derived measurements such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;target successes,&lt;/li&gt;
&lt;li&gt;wrong-target events,&lt;/li&gt;
&lt;li&gt;fallback events,&lt;/li&gt;
&lt;li&gt;calibration error,&lt;/li&gt;
&lt;li&gt;confidence,&lt;/li&gt;
&lt;li&gt;false activations,&lt;/li&gt;
&lt;li&gt;accidental clicks,&lt;/li&gt;
&lt;li&gt;pointer acquisition time,&lt;/li&gt;
&lt;li&gt;recovery after signal loss,&lt;/li&gt;
&lt;li&gt;display resolution and scaling,&lt;/li&gt;
&lt;li&gt;OS/session,&lt;/li&gt;
&lt;li&gt;broad camera category.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We do &lt;strong&gt;not&lt;/strong&gt; need public contributors to upload:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;face photos,&lt;/li&gt;
&lt;li&gt;face videos,&lt;/li&gt;
&lt;li&gt;raw webcam frames,&lt;/li&gt;
&lt;li&gt;private screen contents,&lt;/li&gt;
&lt;li&gt;private dictated text,&lt;/li&gt;
&lt;li&gt;medical information,&lt;/li&gt;
&lt;li&gt;passwords or tokens,&lt;/li&gt;
&lt;li&gt;device serial numbers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is another important boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Public GitHub QA is not automatically research participation
&lt;/h2&gt;

&lt;p&gt;We eventually want good evidence about whether these interfaces actually work.&lt;/p&gt;

&lt;p&gt;But a person who posts an engineering result on GitHub should not silently become a participant in a research paper.&lt;/p&gt;

&lt;p&gt;The programme therefore separates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CI / synthetic evidence
        ↓
community engineering QA
        ↓
separately consented research
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A future human-participant study should have its own protocol and consent process.&lt;/p&gt;

&lt;p&gt;That separation is already part of the planning work.&lt;/p&gt;




&lt;h1&gt;
  
  
  “It worked on my laptop” is not enough
&lt;/h1&gt;

&lt;p&gt;Camera interaction can vary dramatically between environments.&lt;/p&gt;

&lt;p&gt;We eventually need evidence across:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Windows,&lt;/li&gt;
&lt;li&gt;macOS,&lt;/li&gt;
&lt;li&gt;Linux X11,&lt;/li&gt;
&lt;li&gt;GNOME Wayland,&lt;/li&gt;
&lt;li&gt;KDE Wayland,&lt;/li&gt;
&lt;li&gt;different webcams,&lt;/li&gt;
&lt;li&gt;different laptops,&lt;/li&gt;
&lt;li&gt;different screen resolutions,&lt;/li&gt;
&lt;li&gt;display scaling,&lt;/li&gt;
&lt;li&gt;multi-monitor configurations,&lt;/li&gt;
&lt;li&gt;different people,&lt;/li&gt;
&lt;li&gt;repeated sessions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The programme therefore proposes an evidence ladder ranging from pure deterministic tests to real hardware and, separately, controlled human studies.&lt;/p&gt;

&lt;p&gt;The basic idea is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pure unit/state tests
        ↓
cross-platform contract tests
        ↓
synthetic trace replay
        ↓
one real computer
        ↓
another physical computer
        ↓
different people
        ↓
repeated sessions
        ↓
controlled research study
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A green unit test does not prove webcam usability.&lt;/p&gt;

&lt;p&gt;One working laptop does not prove Windows, macOS or Linux support.&lt;/p&gt;

&lt;p&gt;And 1,000 trials from one person are still not 1,000 participants.&lt;/p&gt;




&lt;h1&gt;
  
  
  We also want to explore semantic grounding
&lt;/h1&gt;

&lt;p&gt;One research/development direction is especially interesting.&lt;/p&gt;

&lt;p&gt;Instead of asking the webcam to become dramatically more precise, perhaps precision can come from combining &lt;strong&gt;coarse spatial information with UI semantics&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Imagine looking approximately toward a group of controls and saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“click Save”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The gaze or head pointer gives an approximate region.&lt;/p&gt;

&lt;p&gt;The operating system accessibility tree can provide structured nearby candidates.&lt;/p&gt;

&lt;p&gt;Speech provides the requested operation.&lt;/p&gt;

&lt;p&gt;Then the resolver can produce:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;grounded target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ambiguous — don't guess
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;unresolved — don't act
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;coarse gaze / pointer location
              +
structured UI candidates
              +
spoken intent
              ↓
       target resolver
              ↓
 grounded / ambiguous / unresolved
              ↓
         safety policy
              ↓
            action
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The current proposal deliberately starts with structured accessibility information rather than making screenshots, OCR or vision-language models the default.&lt;/p&gt;

&lt;p&gt;If structured UI data is insufficient, we should &lt;strong&gt;measure that gap first&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Where are we today?
&lt;/h1&gt;

&lt;p&gt;This is important:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The complete hands-free system described in this post is not released.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As of &lt;strong&gt;September 23, 2026&lt;/strong&gt;, the architecture, implementation sequence, evaluation model and contributor workflow are being reviewed in:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/MSKazemi/yazses/pull/413" rel="noopener noreferrer"&gt;https://github.com/MSKazemi/yazses/pull/413&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The umbrella hands-free epic is:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/MSKazemi/yazses/issues/102" rel="noopener noreferrer"&gt;https://github.com/MSKazemi/yazses/issues/102&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PR #413 is a &lt;strong&gt;planning / architecture / evaluation PR&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It does &lt;strong&gt;not&lt;/strong&gt; itself ship:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Head-Pointer runtime,&lt;/li&gt;
&lt;li&gt;Face-Gesture Switch runtime,&lt;/li&gt;
&lt;li&gt;dedicated eye-tracker support,&lt;/li&gt;
&lt;li&gt;a completed hands-free preset,&lt;/li&gt;
&lt;li&gt;research results,&lt;/li&gt;
&lt;li&gt;or new webcam data collection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separation is deliberate.&lt;/p&gt;

&lt;p&gt;We want the architecture, privacy rules, safety boundaries, testing approach and dependencies to be understandable &lt;strong&gt;before&lt;/strong&gt; dozens of implementation PRs start landing.&lt;/p&gt;

&lt;p&gt;There is also active repository coordination work around the design records, including an ADR-numbering collision tracked in:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/MSKazemi/yazses/issues/524" rel="noopener noreferrer"&gt;https://github.com/MSKazemi/yazses/issues/524&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So please treat the &lt;strong&gt;live issue status&lt;/strong&gt; as authoritative rather than assuming every visible eye-control issue is ready for implementation.&lt;/p&gt;




&lt;h1&gt;
  
  
  We are looking for contributors
&lt;/h1&gt;

&lt;p&gt;This is the main reason for this post.&lt;/p&gt;

&lt;p&gt;If you have experience with any of these areas, we would really value your help:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python&lt;/li&gt;
&lt;li&gt;MediaPipe&lt;/li&gt;
&lt;li&gt;computer vision&lt;/li&gt;
&lt;li&gt;assistive technology&lt;/li&gt;
&lt;li&gt;accessibility engineering&lt;/li&gt;
&lt;li&gt;HCI&lt;/li&gt;
&lt;li&gt;gaze interaction&lt;/li&gt;
&lt;li&gt;head tracking&lt;/li&gt;
&lt;li&gt;switch access&lt;/li&gt;
&lt;li&gt;AAC&lt;/li&gt;
&lt;li&gt;Windows UI Automation&lt;/li&gt;
&lt;li&gt;macOS Accessibility APIs&lt;/li&gt;
&lt;li&gt;Linux AT-SPI&lt;/li&gt;
&lt;li&gt;X11&lt;/li&gt;
&lt;li&gt;Wayland&lt;/li&gt;
&lt;li&gt;XDG Desktop Portal / RemoteDesktop&lt;/li&gt;
&lt;li&gt;pointer/input architecture&lt;/li&gt;
&lt;li&gt;testing&lt;/li&gt;
&lt;li&gt;privacy engineering&lt;/li&gt;
&lt;li&gt;human-subject evaluation&lt;/li&gt;
&lt;li&gt;technical documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You do &lt;strong&gt;not&lt;/strong&gt; need expertise in everything above.&lt;/p&gt;

&lt;p&gt;The roadmap is intentionally decomposed so contributors can work on small pieces.&lt;/p&gt;

&lt;p&gt;Expected early implementation areas after the planning gate include things such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;shared perception signal contracts,&lt;/li&gt;
&lt;li&gt;a platform-neutral &lt;code&gt;PointerSink&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;deterministic evaluation schemas,&lt;/li&gt;
&lt;li&gt;safe gaze-calibration plumbing,&lt;/li&gt;
&lt;li&gt;accessibility-tree target contracts,&lt;/li&gt;
&lt;li&gt;privacy-safe evaluation tooling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But please check the issue before starting.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule is simple
&lt;/h2&gt;

&lt;p&gt;For eye-control work:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start an implementation issue only when its live issue says &lt;code&gt;READY&lt;/code&gt; and has the &lt;code&gt;help wanted&lt;/code&gt; label.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For coding-agent work, the repository uses an additional &lt;code&gt;agent-ready&lt;/code&gt; gate.&lt;/p&gt;

&lt;p&gt;A visible &lt;strong&gt;PLANNED&lt;/strong&gt; issue exists so people can understand the roadmap. It does not mean the dependencies are ready.&lt;/p&gt;




&lt;h1&gt;
  
  
  You will also be able to help without writing code
&lt;/h1&gt;

&lt;p&gt;Real hardware testing will eventually be extremely important.&lt;/p&gt;

&lt;p&gt;The project has planned small validation slots for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Windows,&lt;/li&gt;
&lt;li&gt;macOS,&lt;/li&gt;
&lt;li&gt;GNOME Wayland,&lt;/li&gt;
&lt;li&gt;KDE Wayland,&lt;/li&gt;
&lt;li&gt;X11,&lt;/li&gt;
&lt;li&gt;HiDPI,&lt;/li&gt;
&lt;li&gt;multi-monitor setups,&lt;/li&gt;
&lt;li&gt;repeated-session testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those tasks are intentionally being kept small.&lt;/p&gt;

&lt;p&gt;The goal is eventually to let somebody run one defined test pack on one computer and report:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PASS / PARTIAL / FAIL / BLOCKED&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A failure is useful evidence.&lt;/p&gt;

&lt;p&gt;But those hardware slots are &lt;strong&gt;not ready yet&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;They depend on the local evaluator and relevant runtime/safety features being completed first.&lt;/p&gt;

&lt;p&gt;Please don't invent your own data-collection process for them.&lt;/p&gt;




&lt;h1&gt;
  
  
  Accessibility experience is as valuable as code
&lt;/h1&gt;

&lt;p&gt;There is another kind of contribution that matters even more.&lt;/p&gt;

&lt;p&gt;If you use or support someone who uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;eye tracking,&lt;/li&gt;
&lt;li&gt;switch access,&lt;/li&gt;
&lt;li&gt;AAC,&lt;/li&gt;
&lt;li&gt;head tracking,&lt;/li&gt;
&lt;li&gt;dwell interaction,&lt;/li&gt;
&lt;li&gt;alternative pointing devices,&lt;/li&gt;
&lt;li&gt;hands-free computing,&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;we would like to hear from you.&lt;/p&gt;

&lt;p&gt;Developers should not decide accessibility defaults simply by asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“What feels comfortable to me?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Questions such as these need experience and evidence:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How long should dwell be?&lt;/li&gt;
&lt;li&gt;Which facial gestures are practical?&lt;/li&gt;
&lt;li&gt;Which gestures cause fatigue?&lt;/li&gt;
&lt;li&gt;What should happen when tracking disappears?&lt;/li&gt;
&lt;li&gt;How should pause/recovery work?&lt;/li&gt;
&lt;li&gt;What causes accidental activation?&lt;/li&gt;
&lt;li&gt;What feedback is necessary?&lt;/li&gt;
&lt;li&gt;Which controls must always remain independently disableable?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If our assumptions are wrong, finding that out &lt;strong&gt;before implementation&lt;/strong&gt; is much cheaper than finding it out after release.&lt;/p&gt;




&lt;h1&gt;
  
  
  How to get involved right now
&lt;/h1&gt;

&lt;p&gt;Start here:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;YazSes repository&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/MSKazemi/yazses" rel="noopener noreferrer"&gt;https://github.com/MSKazemi/yazses&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then look at:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Hands-free / accessibility epic #102&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/MSKazemi/yazses/issues/102" rel="noopener noreferrer"&gt;https://github.com/MSKazemi/yazses/issues/102&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Eye/camera architecture and evaluation PR #413&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/MSKazemi/yazses/pull/413" rel="noopener noreferrer"&gt;https://github.com/MSKazemi/yazses/pull/413&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;General contributor guide&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://mskazemi.com/yazses/contribute/start.html" rel="noopener noreferrer"&gt;https://mskazemi.com/yazses/contribute/start.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Right now, reviewing PR #413 and challenging the design is genuinely useful.&lt;/p&gt;

&lt;p&gt;Tell us:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what assumption is wrong,&lt;/li&gt;
&lt;li&gt;which failure mode we missed,&lt;/li&gt;
&lt;li&gt;which platform API we should use,&lt;/li&gt;
&lt;li&gt;whether the proposed abstraction is unnecessarily complicated,&lt;/li&gt;
&lt;li&gt;whether an accessibility interaction would be uncomfortable,&lt;/li&gt;
&lt;li&gt;whether the test plan measures the wrong thing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A thoughtful:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This architecture will fail because…”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;can be more valuable than a large pull request.&lt;/p&gt;




&lt;h1&gt;
  
  
  What I hope we can build
&lt;/h1&gt;

&lt;p&gt;The goal is not an impressive webcam demo.&lt;/p&gt;

&lt;p&gt;The goal is a practical, open-source &lt;strong&gt;multimodal hands-free desktop control layer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Something where a person can:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Speak to create text.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Glance to provide context.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Move using head pose.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Commit using a deliberate switch, dwell, voice or another input.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Combine modalities instead of depending on one perfect sensor.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stop the system immediately when needed.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fall back safely when confidence disappears.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And do the processing locally on their own computer.&lt;/p&gt;

&lt;p&gt;There is a lot left to build.&lt;/p&gt;

&lt;p&gt;That's exactly why we are inviting people in now, while the architecture and implementation boundaries are still open to improvement.&lt;/p&gt;

&lt;p&gt;If this is an area you care about, please look at the repository, read the plan, challenge our assumptions, and tell us where you would like to help.&lt;/p&gt;

&lt;p&gt;⭐ &lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/MSKazemi/yazses" rel="noopener noreferrer"&gt;https://github.com/MSKazemi/yazses&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👁️ &lt;strong&gt;Hands-free epic:&lt;/strong&gt; &lt;a href="https://github.com/MSKazemi/yazses/issues/102" rel="noopener noreferrer"&gt;https://github.com/MSKazemi/yazses/issues/102&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🏗️ &lt;strong&gt;Current eye-control planning PR:&lt;/strong&gt; &lt;a href="https://github.com/MSKazemi/yazses/pull/413" rel="noopener noreferrer"&gt;https://github.com/MSKazemi/yazses/pull/413&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Developers, accessibility users, researchers, testers and documentation contributors are all welcome.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>opensource</category>
      <category>showdev</category>
      <category>software</category>
    </item>
    <item>
      <title>IDKMesh: What if AI agents had to prove their work?</title>
      <dc:creator>Mohsen Seyedkazemi Ardebili</dc:creator>
      <pubDate>Mon, 21 Sep 2026 19:15:11 +0000</pubDate>
      <link>https://dev.to/mskazemi/idkmesh-what-if-ai-agents-had-to-prove-their-work-5bg4</link>
      <guid>https://dev.to/mskazemi/idkmesh-what-if-ai-agents-had-to-prove-their-work-5bg4</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%2Frr7mgdxeketjk1hdfupa.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%2Frr7mgdxeketjk1hdfupa.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;A few days ago I wrote about one result from IDKMesh: a panel of 25 verifiers that, after measuring their shared errors, was effectively worth only one independent verifier.&lt;/p&gt;

&lt;p&gt;That experiment is interesting, but it starts in the middle of the story.&lt;/p&gt;

&lt;p&gt;So this post is the simpler version:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is IDKMesh, why am I building it, and how is it supposed to work?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;AI agents are getting very good at producing things.&lt;/p&gt;

&lt;p&gt;They can write code, edit files, call tools, generate tests, open pull requests, and sometimes operate infrastructure.&lt;/p&gt;

&lt;p&gt;But there is a basic problem:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An agent saying “I finished the task” is not the same as evidence that the task was done correctly.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If we scale from one agent to ten agents, or one hundred agents, generation becomes faster.&lt;/p&gt;

&lt;p&gt;Verification can become the bottleneck.&lt;/p&gt;

&lt;p&gt;And if all of those agents or reviewers make similar mistakes, adding more of them may only give us more confidence, not more truth.&lt;/p&gt;

&lt;p&gt;That is the problem IDKMesh is exploring.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is IDKMesh?
&lt;/h2&gt;

&lt;p&gt;IDKMesh is an open-source research and engineering project about coordinating:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;humans,&lt;/li&gt;
&lt;li&gt;AI agents,&lt;/li&gt;
&lt;li&gt;software tools,&lt;/li&gt;
&lt;li&gt;verifiers,&lt;/li&gt;
&lt;li&gt;and different kinds of compute&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;around uncertain work.&lt;/p&gt;

&lt;p&gt;The name comes from a simple idea:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I don’t know. You don’t know. Together, the mesh can discover, build, verify, and learn.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For me, the most important word is not “mesh.”&lt;/p&gt;

&lt;p&gt;It is &lt;strong&gt;verify&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;IDKMesh tries to separate the thing that does the work from the thing that decides whether the work should be trusted.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple example
&lt;/h2&gt;

&lt;p&gt;Imagine I give an AI agent this task:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Fix a bug in a Python project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A normal agent workflow might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Task
  ↓
AI agent
  ↓
Code change
  ↓
“Done”
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is useful, but the same system that created the answer is also telling us the answer is good.&lt;/p&gt;

&lt;p&gt;IDKMesh is aiming for something more like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Human goal
    ↓
Bounded Work Unit
    ↓
One or more replaceable workers
    ↓
Candidate results
    ↓
Independent verification
    ↓
Recorded evidence
    ↓
Human / governance decision
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The worker can propose.&lt;/p&gt;

&lt;p&gt;The verifier can check.&lt;/p&gt;

&lt;p&gt;The evidence can be inspected later.&lt;/p&gt;

&lt;p&gt;And the final authority stays separate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Worker success != acceptance.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That small rule changes a lot.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;At a high level, IDKMesh uses a few simple building blocks.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Give the worker a bounded job
&lt;/h3&gt;

&lt;p&gt;Instead of giving an agent unlimited authority over a project, work is described as a &lt;strong&gt;Work Unit&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The task says what needs to be done, what the boundaries are, and what security constraints apply.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Let workers produce candidates
&lt;/h3&gt;

&lt;p&gt;A worker is allowed to try.&lt;/p&gt;

&lt;p&gt;There can be multiple attempts.&lt;/p&gt;

&lt;p&gt;The first answer does not need to become the final answer.&lt;/p&gt;

&lt;p&gt;Workers are replaceable. Their output is a candidate.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Verify separately
&lt;/h3&gt;

&lt;p&gt;A verifier should not simply trust the worker’s own completion claim.&lt;/p&gt;

&lt;p&gt;Verification has its own plan and produces its own result.&lt;/p&gt;

&lt;p&gt;This is where tests, independent reviewers, policy checks, or other evidence can enter.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Keep the evidence
&lt;/h3&gt;

&lt;p&gt;IDKMesh treats provenance and verification results as real artifacts, not temporary chat messages.&lt;/p&gt;

&lt;p&gt;The goal is to be able to ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What was the task?&lt;/li&gt;
&lt;li&gt;Who or what produced this result?&lt;/li&gt;
&lt;li&gt;What checked it?&lt;/li&gt;
&lt;li&gt;Which evidence supported the decision?&lt;/li&gt;
&lt;li&gt;Can we replay or inspect it later?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Keep the final decision explicit
&lt;/h3&gt;

&lt;p&gt;A worker does not get merge authority just because it says “success.”&lt;/p&gt;

&lt;p&gt;A verifier recommendation is also not automatically the final decision.&lt;/p&gt;

&lt;p&gt;The integration step stays explicit.&lt;/p&gt;

&lt;p&gt;**&lt;/p&gt;

&lt;p&gt;**&lt;/p&gt;

&lt;h2&gt;
  
  
  What can you actually use today?
&lt;/h2&gt;

&lt;p&gt;This part is important because I do not want to describe research as a finished product.&lt;/p&gt;

&lt;p&gt;The full &lt;strong&gt;Verified Swarm Runner is not yet a production-ready multi-agent platform&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The repository is still a research and engineering foundation.&lt;/p&gt;

&lt;p&gt;But one useful tool is already available: &lt;strong&gt;&lt;code&gt;idkmesh gate-audit&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It takes verdicts from a review panel you already have and asks a practical question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How many independent votes is this panel actually worth?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The repository also contains experiments, schemas, validators, simulations, interoperability work, and pieces of the future runner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The repository is private for now, so the clone and run commands that used to be here cannot be run from outside. The research code and its demo will be easier to try once the project is public.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I want IDKMesh to become
&lt;/h2&gt;

&lt;p&gt;The long-term idea is bigger than a review tool.&lt;/p&gt;

&lt;p&gt;I want to explore whether large groups of humans and AI agents can work on real problems without making trust an afterthought.&lt;/p&gt;

&lt;p&gt;That means learning how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;divide work into bounded tasks,&lt;/li&gt;
&lt;li&gt;run multiple independent attempts,&lt;/li&gt;
&lt;li&gt;use different workers and tools,&lt;/li&gt;
&lt;li&gt;measure whether verification is actually independent,&lt;/li&gt;
&lt;li&gt;keep reproducible evidence,&lt;/li&gt;
&lt;li&gt;learn from failures,&lt;/li&gt;
&lt;li&gt;and scale without giving autonomous workers unlimited authority.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I do not know yet how far this model can scale.&lt;/p&gt;

&lt;p&gt;That uncertainty is part of the project.&lt;/p&gt;

&lt;p&gt;The repository is designed to test those questions rather than pretend they are already solved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why open source?
&lt;/h2&gt;

&lt;p&gt;Because a system about verification should itself be inspectable.&lt;/p&gt;

&lt;p&gt;If IDKMesh makes a claim, I want the experiment, code, result, or limitation to be visible.&lt;/p&gt;

&lt;p&gt;And I especially want other people to find the assumptions that are wrong.&lt;/p&gt;

&lt;p&gt;If you are interested in AI agents, verification, distributed systems, CI, developer tooling, or human-in-the-loop systems, take a look at the project on GitHub, which is private for now.&lt;/p&gt;

&lt;p&gt;You do not need to understand the whole project to contribute.&lt;/p&gt;

&lt;p&gt;Even showing where the newcomer path is confusing is useful evidence.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>architecture</category>
      <category>llm</category>
    </item>
    <item>
      <title>Our linter's "safe" autofix would have silently disabled RBAC</title>
      <dc:creator>Mohsen Seyedkazemi Ardebili</dc:creator>
      <pubDate>Sun, 20 Sep 2026 21:54:58 +0000</pubDate>
      <link>https://dev.to/mskazemi/our-linters-safe-autofix-would-have-silently-disabled-rbac-log</link>
      <guid>https://dev.to/mskazemi/our-linters-safe-autofix-would-have-silently-disabled-rbac-log</guid>
      <description>&lt;p&gt;KubeIntellect is an AI agent that runs &lt;code&gt;kubectl&lt;/code&gt; against a live cluster, so its tools carry a role check and a human-approval gate. Both depend on one thing: the tool actually receiving the run config the graph injects. This is the parameter that receives it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;RunnableConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;InjectedToolArg&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# type: ignore[assignment]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That line is wrong in every way a reviewer is trained to notice. The default is &lt;code&gt;None&lt;/code&gt;, but the annotation does not say &lt;code&gt;| None&lt;/code&gt;. mypy complains, which is why there is a &lt;code&gt;type: ignore&lt;/code&gt; sitting on it. Ruff wants to rewrite the &lt;code&gt;Optional[...]&lt;/code&gt; spelling of it. Every instinct says clean this up.&lt;/p&gt;

&lt;p&gt;Cleaning it up disables role-based access control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the annotation is load-bearing
&lt;/h2&gt;

&lt;p&gt;LangChain finds the parameter to inject by walking the type hints and comparing with &lt;code&gt;is&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;type_hints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;type_&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;RunnableConfig&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;name&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is an identity comparison against one exact class object. &lt;code&gt;RunnableConfig | None&lt;/code&gt; is a &lt;code&gt;UnionType&lt;/code&gt;, not that object. It does not match, so no parameter is selected, so nothing is injected.&lt;/p&gt;

&lt;p&gt;Three functions, run against langchain-core 1.6.2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;RunnableConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;InjectedToolArg&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;widened&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;RunnableConfig&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;InjectedToolArg&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;optional_form&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;RunnableConfig&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;InjectedToolArg&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bare           -&amp;gt; injected param: 'config'
widened        -&amp;gt; injected param: None
optional_form  -&amp;gt; injected param: None
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No error. No warning. The tool still runs. It just receives &lt;code&gt;config=None&lt;/code&gt; forever.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is actually lost
&lt;/h2&gt;

&lt;p&gt;The caller's role comes off that config, and the fallback is the problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;user_role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;user_role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;configurable&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the config gone, &lt;code&gt;config&lt;/code&gt; is falsy and every call executes as &lt;code&gt;admin&lt;/code&gt;. A &lt;code&gt;readonly&lt;/code&gt; API key stops being read-only. The check that returns &lt;code&gt;[Permission Denied] Your API key has read-only access&lt;/code&gt; is still there, still covered by its unit tests, still passing — and it never fires, because the role it compares against is no longer the caller's role.&lt;/p&gt;

&lt;p&gt;To be precise about the other half, since overstating this would be easy: the human-approval gate does not break the same way. &lt;code&gt;hitl_bypass&lt;/code&gt; also comes off the config and defaults to &lt;code&gt;False&lt;/code&gt;, so losing the config makes the tool prompt for approval &lt;em&gt;more&lt;/em&gt; often, not less. The RBAC default is the one that fails open.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that makes it a trap rather than a bug
&lt;/h2&gt;

&lt;p&gt;Nobody has to be careless to introduce this. The tooling volunteers it.&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;ruff check &lt;span class="nt"&gt;--select&lt;/span&gt; UP045 probe.py
&lt;span class="go"&gt;UP045 [*] Use `X | None` for type annotations
&lt;/span&gt;&lt;span class="gp"&gt; --&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;probe.py:5:35
&lt;span class="go"&gt;help: Convert to `X | None`
[*] 1 fixable with the `--fix` option.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;[*]&lt;/code&gt; means ruff classifies that rewrite as a &lt;strong&gt;safe&lt;/strong&gt; fix. Not &lt;code&gt;--unsafe-fixes&lt;/code&gt;. Plain &lt;code&gt;ruff check --fix&lt;/code&gt; — the command people run without reading the diff — converts a working authorization boundary into a no-op.&lt;/p&gt;

&lt;p&gt;A behavioral test will not save you either. The tools most likely to get "cleaned up" are the ones that never read &lt;code&gt;config&lt;/code&gt; at all: they pass every test they have while silently receiving &lt;code&gt;None&lt;/code&gt;. We found exactly that shape in four of our own read verbs. Harmless there, because those verbs make no authorization decision — but it is the same defect, one file away from a place where it matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we did instead of a comment
&lt;/h2&gt;

&lt;p&gt;There is a comment on the line. Comments do not fail CI.&lt;/p&gt;

&lt;p&gt;The guard is a test that asserts the annotation itself. It scans every &lt;code&gt;config: Annotated[..., InjectedToolArg]&lt;/code&gt; parameter in the source and fails if any is not bare &lt;code&gt;RunnableConfig&lt;/code&gt;. Alongside it are two dynamic canaries that build a real tool with each spelling and assert, against whatever langchain version is actually installed, that the required form &lt;em&gt;is&lt;/em&gt; injected and the forbidden form is &lt;em&gt;not&lt;/em&gt; — so the day the library changes its matching rule, a test says so instead of production.&lt;/p&gt;

&lt;p&gt;There is also a test asserting the scanner finds the known sites, because a regex that matched nothing would make every other assertion in the file vacuously true.&lt;/p&gt;

&lt;h2&gt;
  
  
  The general version
&lt;/h2&gt;

&lt;p&gt;When a framework dispatches on type identity, your annotation is not documentation. It is runtime configuration written in the type language — and anything that "improves" your types can change behavior: a linter, a type checker, an IDE quick-fix, or an agent asked to clean up implicit &lt;code&gt;Optional&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you have a line like that, the fix is not a louder comment. It is a test that fails when someone improves it.&lt;/p&gt;

&lt;p&gt;Code: &lt;a href="https://github.com/MSKazemi/kubeintellect" rel="noopener noreferrer"&gt;https://github.com/MSKazemi/kubeintellect&lt;/a&gt;&lt;br&gt;
The guard: &lt;code&gt;v4/tests/test_injected_config_invariant.py&lt;/code&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>security</category>
      <category>kubernetes</category>
    </item>
  </channel>
</rss>
