<?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: Malek AIT OUARET</title>
    <description>The latest articles on DEV Community by Malek AIT OUARET (@malek_aitouaret_f026758c).</description>
    <link>https://dev.to/malek_aitouaret_f026758c</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%2F4192110%2F3f472050-c40f-4442-900a-f7ab6bf0cf24.png</url>
      <title>DEV Community: Malek AIT OUARET</title>
      <link>https://dev.to/malek_aitouaret_f026758c</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/malek_aitouaret_f026758c"/>
    <language>en</language>
    <item>
      <title>Green proofs, empty promises: when AI agents write their own specifications</title>
      <dc:creator>Malek AIT OUARET</dc:creator>
      <pubDate>Wed, 21 Oct 2026 07:44:35 +0000</pubDate>
      <link>https://dev.to/malek_aitouaret_f026758c/green-proofs-empty-promises-when-ai-agents-write-their-own-specifications-59lf</link>
      <guid>https://dev.to/malek_aitouaret_f026758c/green-proofs-empty-promises-when-ai-agents-write-their-own-specifications-59lf</guid>
      <description>&lt;p&gt;I asked AI coding agents to write &lt;strong&gt;formally verified&lt;/strong&gt; code. Every solution passed the&lt;br&gt;
verifier. Then I checked what the proofs actually promised.&lt;/p&gt;

&lt;p&gt;For the smaller model, &lt;strong&gt;5 of 8 specifications barely said anything&lt;/strong&gt;. One of the&lt;br&gt;
methods was wrong, and its proof was green.&lt;/p&gt;

&lt;p&gt;This post is about that gap, a cheap way to detect it, and a small open-source tool that does it&lt;br&gt;
for any Dafny file.&lt;/p&gt;
&lt;h2&gt;
  
  
  The experiment
&lt;/h2&gt;

&lt;p&gt;I'm designing a programming language meant to be written by agents and checked by machines&lt;br&gt;
(more on that at the end). Before designing anything, I wanted data: &lt;strong&gt;when an agent has to prove&lt;br&gt;
its code, do fewer bugs slip through?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The setup (&lt;a href="https://github.com/quod-lang/quod/tree/main/bench" rel="noopener noreferrer"&gt;VerifBench&lt;/a&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;8 short algorithmic tasks chosen because LLMs classically stumble on them: Gregorian
day counting (leap centuries), strict UTF-8 validation (overlongs, surrogates), glob matching
with escapes, an LRU cache, a lexicographically smallest topological sort, text justification,
big-number addition (borrows, &lt;code&gt;-0&lt;/code&gt;), Unix path normalization.&lt;/li&gt;
&lt;li&gt;Same natural-language spec, two arms: plain &lt;strong&gt;Python&lt;/strong&gt; (the agent may run its own tests), or
&lt;strong&gt;Dafny&lt;/strong&gt;, where the agent must write &lt;code&gt;requires&lt;/code&gt; / &lt;code&gt;ensures&lt;/code&gt; / invariants and get
&lt;code&gt;dafny verify&lt;/code&gt; to pass. Cheats like &lt;code&gt;assume&lt;/code&gt; or &lt;code&gt;{:verify false}&lt;/code&gt; are detected.&lt;/li&gt;
&lt;li&gt;Claude Code as the agent, two models: &lt;code&gt;claude-opus-5-5&lt;/code&gt; and &lt;code&gt;claude-haiku-4-5&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;About 500 &lt;strong&gt;hidden tests&lt;/strong&gt; per task, compared with an oracle. The Dafny code is compiled to
Python and goes through exactly the same tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key metric is the &lt;strong&gt;escaped bug&lt;/strong&gt;: a solution the agent would ship (and, in Dafny, that&lt;br&gt;
verifies) which still fails a hidden test.&lt;/p&gt;
&lt;h2&gt;
  
  
  The results look reassuring...
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model / arm&lt;/th&gt;
&lt;th&gt;Escaped bugs&lt;/th&gt;
&lt;th&gt;Cost per task*&lt;/th&gt;
&lt;th&gt;Time per task&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Opus / Python&lt;/td&gt;
&lt;td&gt;0 / 8&lt;/td&gt;
&lt;td&gt;$0.14&lt;/td&gt;
&lt;td&gt;17 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Opus / Dafny&lt;/td&gt;
&lt;td&gt;0 / 8&lt;/td&gt;
&lt;td&gt;$0.22&lt;/td&gt;
&lt;td&gt;46 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Haiku / Python&lt;/td&gt;
&lt;td&gt;1 / 8&lt;/td&gt;
&lt;td&gt;$0.15&lt;/td&gt;
&lt;td&gt;106 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Haiku / Dafny&lt;/td&gt;
&lt;td&gt;1 / 8&lt;/td&gt;
&lt;td&gt;$0.45&lt;/td&gt;
&lt;td&gt;255 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;small&gt;* API-equivalent estimate reported by the agent. One trial per cell: signals, not statistics.&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;Opus solved everything, with or without proofs. Haiku let one bug through in each arm. Proof&lt;br&gt;
looks like it costs 1.5–3× more and buys... nothing here?&lt;/p&gt;
&lt;h2&gt;
  
  
  ...until you read the Haiku Dafny bug
&lt;/h2&gt;

&lt;p&gt;The escaped Dafny bug was in the LRU cache. The method &lt;strong&gt;verified&lt;/strong&gt;. Here is its signature&lt;br&gt;
(preconditions abridged) and its entire postcondition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dafny"&gt;&lt;code&gt;&lt;span class="kr"&gt;method&lt;/span&gt; &lt;span class="n"&gt;lru_cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ops&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;seq&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&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="p"&gt;)&lt;/span&gt; &lt;span class="kr"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;seq&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kr"&gt;requires&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="kr"&gt;ensures&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;CountGets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ops&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ops&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output has the right &lt;em&gt;length&lt;/em&gt;. Nothing says which values a &lt;code&gt;get&lt;/code&gt; returns. The code evicted&lt;br&gt;
an entry every time a new key was inserted, whether the cache was full or not, and Dafny was&lt;br&gt;
perfectly happy, because nothing in the contract forbade it.&lt;/p&gt;

&lt;p&gt;A verifier only guarantees what you ask it to. And when the agent writes &lt;strong&gt;both&lt;/strong&gt; the code and&lt;br&gt;
the specification, it can, without any malice, ask for exactly as much as it manages to prove.&lt;/p&gt;

&lt;p&gt;So I went back and checked all 24 Dafny contracts.&lt;/p&gt;
&lt;h2&gt;
  
  
  A simple question to ask a specification
&lt;/h2&gt;

&lt;p&gt;For a method with precondition &lt;code&gt;R&lt;/code&gt; and postcondition &lt;code&gt;E&lt;/code&gt;, ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can two &lt;strong&gt;different&lt;/strong&gt; results both satisfy &lt;code&gt;E&lt;/code&gt;, for the same valid input?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the contract is complete, the answer is no:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ocl"&gt;&lt;code&gt;&lt;span class="err"&gt;∀&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;∧&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;∧&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="err"&gt;⇒&lt;/span&gt;  &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a question about the &lt;strong&gt;contract only&lt;/strong&gt;, not the code. And Dafny can answer it: generate&lt;br&gt;
a lemma with exactly that statement and ask Dafny to prove it, with no hints.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dafny"&gt;&lt;code&gt;&lt;span class="kr"&gt;ghost&lt;/span&gt; &lt;span class="kr"&gt;predicate&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ops&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;seq&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kt"&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="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;seq&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kr"&gt;requires&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;CountGets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ops&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ops&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;lemma&lt;/span&gt; &lt;span class="n"&gt;Uniq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ops&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;seq&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kt"&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="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;seq&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;seq&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kr"&gt;requires&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ops&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ops&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kr"&gt;ensures&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the proof goes through, the contract determines the result. When it fails, that may mean&lt;br&gt;
the contract is incomplete... or just that the uniqueness proof is hard (it often needs&lt;br&gt;
induction). To tell the two apart, you need a &lt;strong&gt;witness&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Finding witnesses
&lt;/h2&gt;

&lt;p&gt;The trick: take small concrete inputs, &lt;strong&gt;run the method&lt;/strong&gt; to get its actual answer &lt;code&gt;a&lt;/code&gt;, build&lt;br&gt;
neighbouring answers &lt;code&gt;b&lt;/code&gt; (a changed digit, a dropped element, an emptied list...), and ask Dafny&lt;br&gt;
to &lt;em&gt;prove&lt;/em&gt; that the contract accepts both &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; on those concrete values.&lt;/p&gt;

&lt;p&gt;If both proofs go through, you have a certain witness: no sampling, no guessing. Results on the&lt;br&gt;
24 agent-written contracts:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Verdict&lt;/th&gt;
&lt;th&gt;Count&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Uniqueness proved&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Underspecified, with a witness&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inconclusive&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The six witnesses:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Two results the contract accepts&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;add_big&lt;/code&gt; (Haiku)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"0" + "0"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;"0"&lt;/code&gt; and &lt;code&gt;"1"&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;topo_sort&lt;/code&gt; (Haiku)&lt;/td&gt;
&lt;td&gt;1 node, no edge&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;[0]&lt;/code&gt; and &lt;code&gt;[]&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;normalize_path&lt;/code&gt; (Haiku)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;""&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;"."&lt;/code&gt; and &lt;code&gt;".x"&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;justify&lt;/code&gt; (Haiku)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;["a"]&lt;/code&gt;, width 2&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;["a "]&lt;/code&gt; and &lt;code&gt;[]&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;lru_cache&lt;/code&gt; (Haiku)&lt;/td&gt;
&lt;td&gt;two &lt;code&gt;get&lt;/code&gt;s on an empty cache&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;[-1, -1]&lt;/code&gt; and &lt;code&gt;[0, -1]&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;binary_search&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;[1, 1]&lt;/code&gt;, target 1&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;1&lt;/code&gt; and &lt;code&gt;0&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Read them as sentences and they speak for themselves: according to its proved contract, the&lt;br&gt;
addition is allowed to say 0 + 0 = 1. The topological sort may return nothing for a graph with&lt;br&gt;
no edge. The path normalizer may return any non-empty string.&lt;/p&gt;

&lt;p&gt;The last one, &lt;code&gt;binary_search&lt;/code&gt;, is &lt;strong&gt;legitimately&lt;/strong&gt; free: with duplicates, any index of the target&lt;br&gt;
is a correct answer. A witness doesn't mean "bug"; it means "the contract allows a choice", and&lt;br&gt;
someone should decide whether that choice is intended.&lt;/p&gt;

&lt;p&gt;The striking part: &lt;strong&gt;four of Haiku's five near-empty contracts sat on correct code&lt;/strong&gt;. The tests&lt;br&gt;
passed, the proofs were green, and the proofs guaranteed almost nothing. Without this check,&lt;br&gt;
"verified" on those methods was pure decoration.&lt;/p&gt;

&lt;p&gt;And the three inconclusive ones? I read them: their contracts &lt;em&gt;are&lt;/em&gt; complete (merging intervals&lt;br&gt;
into a canonical union, run-length encoding, canonical decimal strings), but proving uniqueness&lt;br&gt;
needs induction that the solver won't find alone. No witness was found for any of them, so the&lt;br&gt;
tool never reported them as underspecified. Clean separation, on this small sample.&lt;/p&gt;

&lt;p&gt;Opus, for the record, wrote contracts that were proved unique in 7 of 8 hard tasks, including a&lt;br&gt;
full lexicographic-minimality specification for the topological sort.&lt;/p&gt;
&lt;h2&gt;
  
  
  The tool: dafny-complete
&lt;/h2&gt;

&lt;p&gt;I turned this into a standalone checker that works on any &lt;code&gt;.dfy&lt;/code&gt; file:&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; &lt;span class="s2"&gt;"git+https://github.com/quod-lang/quod#subdirectory=tools/dafny-complete"&lt;/span&gt;
dafny-complete my_file.dfy
&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;  [OK]    Abs      line 11    the contract determines a unique result
  [UNDER] WeakAbs  line 19    the contract accepts two different results
                    input  : x=0
                    y = 0   (what the method returns)
                    y = 1   (also satisfies every ensures)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It runs four Dafny passes over the whole file (uniqueness lemmas, valid small inputs proved&lt;br&gt;
against the preconditions, actual execution of the methods, witness lemmas) and never modifies&lt;br&gt;
your file. Exit code 1 when something is underspecified, so it can sit in CI, or in an agent's&lt;br&gt;
loop: &lt;code&gt;--json&lt;/code&gt; gives the witness in machine-readable form, which is exactly the feedback an agent&lt;br&gt;
needs to strengthen its contract.&lt;/p&gt;

&lt;p&gt;Without access to the benchmark's oracle, it reproduces all 24 verdicts. Run on the whole&lt;br&gt;
&lt;code&gt;add_big&lt;/code&gt; file rather than just the main method, it also flags the three helpers Haiku wrote&lt;br&gt;
(&lt;code&gt;AddAbsolute&lt;/code&gt; accepts &lt;code&gt;"10" + "10" = "201"&lt;/code&gt;): the weakness was not one slip, but the way the&lt;br&gt;
whole file was specified.&lt;/p&gt;
&lt;h2&gt;
  
  
  Limitations, honestly
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Small sample.&lt;/strong&gt; 8 tasks per suite, one trial per cell, one agent family. The pattern is
vivid, but it is a signal, not a measurement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complete is not correct.&lt;/strong&gt; A contract can pin down a single, wrong result. The check
removes specifications that are too &lt;em&gt;weak&lt;/em&gt;, not ones that are &lt;em&gt;mistaken&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The second-program trap.&lt;/strong&gt; A postcondition like &lt;code&gt;out == Model(ops)&lt;/code&gt;, where &lt;code&gt;Model&lt;/code&gt;
reimplements the problem, is always "complete", and can carry the same bug as the code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Witness search is heuristic.&lt;/strong&gt; Neighbours come from simple rules; a contract can be
incomplete in a subtler way and come out "inconclusive". Supported types for witnesses: &lt;code&gt;int&lt;/code&gt;,
&lt;code&gt;nat&lt;/code&gt;, &lt;code&gt;bool&lt;/code&gt;, &lt;code&gt;char&lt;/code&gt;, &lt;code&gt;string&lt;/code&gt;, &lt;code&gt;seq&amp;lt;T&amp;gt;&lt;/code&gt;, tuples. Instance methods and heap-touching methods
are skipped for now.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Prior work
&lt;/h2&gt;

&lt;p&gt;After publishing, I did the literature pass I should have done before. The core idea is not new:&lt;br&gt;
Shuvendu Lahiri's &lt;a href="https://arxiv.org/abs/2406.09757" rel="noopener noreferrer"&gt;Evaluating LLM-driven User-Intent Formalization for Verification-Aware Languages&lt;/a&gt;&lt;br&gt;
(FMCAD 2024) already tests Dafny specifications symbolically, scoring their completeness by&lt;br&gt;
mutating expected outputs on test cases and checking that the verifier rejects them. Related lines&lt;br&gt;
of work include &lt;a href="https://arxiv.org/abs/2310.01831" rel="noopener noreferrer"&gt;nl2postcond&lt;/a&gt; (completeness of LLM-written&lt;br&gt;
postconditions measured against code mutants), &lt;a href="https://arxiv.org/abs/2310.17807" rel="noopener noreferrer"&gt;Clover&lt;/a&gt;&lt;br&gt;
(consistency between code, docstrings and Dafny annotations), and Lahiri's 2026&lt;br&gt;
&lt;a href="https://arxiv.org/abs/2603.17150" rel="noopener noreferrer"&gt;intent-formalization agenda&lt;/a&gt;, which names validating&lt;br&gt;
specifications as the central bottleneck for AI-written code. Weak or vacuous specifications are&lt;br&gt;
also a documented failure mode in recent verified-code benchmarks.&lt;/p&gt;

&lt;p&gt;What &lt;code&gt;dafny-complete&lt;/code&gt; adds is modest: it needs no tests or reference code (it runs the method&lt;br&gt;
itself), it first tries a universal uniqueness proof, and it only reports a witness when Dafny&lt;br&gt;
proves the contract accepts it. Details in the repo's&lt;br&gt;
&lt;a href="https://github.com/quod-lang/quod/blob/main/docs/related-work.md" rel="noopener noreferrer"&gt;related-work note&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why I care: Quod
&lt;/h2&gt;

&lt;p&gt;This started as Phase 0 of &lt;a href="https://github.com/quod-lang/quod" rel="noopener noreferrer"&gt;Quod&lt;/a&gt;, a language designed to be&lt;br&gt;
written by AI agents and checked by machines. The bet: everything that is expensive for humans&lt;br&gt;
(exhaustive contracts, invariants, declared effects) is nearly free for an agent, so a language&lt;br&gt;
can demand it.&lt;/p&gt;

&lt;p&gt;This experiment changed the design. In Quod, the completeness check is built into the compiler:&lt;br&gt;
an underspecified function &lt;strong&gt;does not compile&lt;/strong&gt;, and the error comes with the witness. When&lt;br&gt;
several answers are legitimately acceptable, you say so explicitly, with a relation between two&lt;br&gt;
acceptable results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;binary_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;i64&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="n"&gt;requires&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;ensures&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="nf"&gt;loose&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;r1&lt;/span&gt;&lt;span class="nf"&gt;.is_some&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;r2&lt;/span&gt;&lt;span class="nf"&gt;.is_some&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;   &lt;span class="c1"&gt;// which index may vary; found/not found may not&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Freedom becomes a visible, checked decision instead of an oversight.&lt;/p&gt;

&lt;p&gt;Everything is open: the benchmark with every agent transcript, the language draft, and the tool&lt;br&gt;
(Apache 2.0). If you write Dafny, I'd love to know what &lt;code&gt;dafny-complete&lt;/code&gt; finds, or misses, on&lt;br&gt;
your code. Issues and discussions are open at&lt;br&gt;
&lt;a href="https://github.com/quod-lang/quod" rel="noopener noreferrer"&gt;github.com/quod-lang/quod&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>dafny</category>
      <category>formalverification</category>
      <category>ai</category>
      <category>llm</category>
    </item>
    <item>
      <title>What I learned using a 7B local LLM as the front end of a programming language</title>
      <dc:creator>Malek AIT OUARET</dc:creator>
      <pubDate>Sun, 18 Oct 2026 12:50:01 +0000</pubDate>
      <link>https://dev.to/malek_aitouaret_f026758c/what-i-learned-using-a-7b-local-llm-as-the-front-end-of-a-programming-language-1pcf</link>
      <guid>https://dev.to/malek_aitouaret_f026758c/what-i-learned-using-a-7b-local-llm-as-the-front-end-of-a-programming-language-1pcf</guid>
      <description>&lt;p&gt;Pseudocode is how we teach algorithms, yet it has never been executable. You can write "repeat divisor from 2 to 50"&lt;br&gt;
on a whiteboard, but not run it. I wanted to change that without inventing yet another syntax, so I built&lt;br&gt;
&lt;strong&gt;Pseudo.NET&lt;/strong&gt;: you write pseudocode however you like, and a &lt;em&gt;local&lt;/em&gt; language model turns it into a program.&lt;/p&gt;

&lt;p&gt;This is not a launch post. It is what I learned about putting a small LLM at the front of a compiler. If you are&lt;br&gt;
tempted to try something similar, maybe some of this saves you a few evenings.&lt;/p&gt;
&lt;h2&gt;
  
  
  The idea in one picture
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;free-form pseudocode  -&amp;gt;  local LLM  -&amp;gt;  JSON AST  -&amp;gt;  validate / repair  -&amp;gt;  deterministic C# backend  -&amp;gt;  normal build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The model never writes the final program. Its job ends at a &lt;strong&gt;structured tree&lt;/strong&gt; (a JSON AST with a fixed set of node&lt;br&gt;
kinds). Everything after that is ordinary, testable code: a validator, a type inference pass, and a backend that emits C#.&lt;br&gt;
That split is the single most important design decision, and everything below follows from it.&lt;/p&gt;

&lt;p&gt;Here is a program, written with tabs only, no braces, no &lt;code&gt;end if&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;We want to find all the prime numbers up to 50.

primes = a new list

define whether counter is prime, which returns a boolean
    isPrime = true
    if counter is less than 2 then
        isPrime = false
    repeat divisor from 2 to 50
        if divisor is less than counter then
            remainder = counter modulo divisor
            if remainder is equal to 0 then
                isPrime = false
    return isPrime

repeat counter from 2 to 50
    candidate = whether counter is prime
    if candidate then
        add counter to primes

print the text "Prime numbers up to 50:"
for each number in primes
    print number
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same program works written in French ("définir est-ce que compteur est premier…"), and in other languages the model&lt;br&gt;
understands. The tool does not decide what the syntax is; the user does.&lt;/p&gt;
&lt;h2&gt;
  
  
  What I used
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A small model, &lt;code&gt;qwen2.5-coder:7b&lt;/code&gt;, run locally through Ollama. No account, no API key, and nothing leaves the machine
(which matters if the users are students).&lt;/li&gt;
&lt;li&gt;Temperature 0. For identical input, the output is the same.&lt;/li&gt;
&lt;li&gt;F# for the compiler, C# as the target, and an MSBuild package so a &lt;code&gt;.pseudo&lt;/code&gt; file builds like any other source file.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Lesson 1: a small model drops bare booleans
&lt;/h2&gt;

&lt;p&gt;My first AST had flags like &lt;code&gt;"tapped": true&lt;/code&gt; for keyboard input ("is the left arrow &lt;em&gt;held&lt;/em&gt; or was it &lt;em&gt;just pressed&lt;/em&gt;?").&lt;br&gt;
The 7B model quietly omitted them. Not always, but often enough that games behaved randomly: a piece that should move&lt;br&gt;
continuously moved once.&lt;/p&gt;

&lt;p&gt;What worked was replacing flags by &lt;strong&gt;named values&lt;/strong&gt;: &lt;code&gt;"press": "held"&lt;/code&gt; or &lt;code&gt;"press": "just_pressed"&lt;/code&gt;, and for drawing&lt;br&gt;
&lt;code&gt;"style": "filled"&lt;/code&gt; or &lt;code&gt;"style": "outline"&lt;/code&gt;. A string with a name is much harder to forget than a missing &lt;code&gt;true&lt;/code&gt;. Field&lt;br&gt;
order mattered too: putting &lt;code&gt;style&lt;/code&gt; right after &lt;code&gt;kind&lt;/code&gt; made the model fill it in far more reliably.&lt;/p&gt;

&lt;p&gt;A simplified example of what the model now returns for "while the left key is held":&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;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"KeyPressed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"left"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"press"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"held"&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;h2&gt;
  
  
  Lesson 2: concrete examples beat abstract rules, but long prompts backfire
&lt;/h2&gt;

&lt;p&gt;Rules written in prose ("a negated condition is a unary NOT") were followed inconsistently. A couple of tiny concrete&lt;br&gt;
JSON examples in the prompt fixed the same problems more reliably.&lt;/p&gt;

&lt;p&gt;The catch: every addition makes the prompt longer, and a longer prompt made the model &lt;strong&gt;forget something else&lt;/strong&gt;. I added a&lt;br&gt;
section about drawing and, a day later, noticed negations had regressed ("if not X" quietly became "X != false" and a game&lt;br&gt;
showed a black screen). The only defence I found was a regression net: a script that regenerates a set of sample programs&lt;br&gt;
from scratch with the real model and checks the generated C# against expectations. Every prompt change goes through it.&lt;br&gt;
If you do this, build that net first, not after the third surprise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 3: decide what you are allowed to repair
&lt;/h2&gt;

&lt;p&gt;Models make mistakes in the &lt;em&gt;shape&lt;/em&gt; of their output (a field named &lt;code&gt;operator&lt;/code&gt; instead of &lt;code&gt;op&lt;/code&gt;, a kind called &lt;code&gt;Call&lt;/code&gt;&lt;br&gt;
instead of &lt;code&gt;FunctionCallStmt&lt;/code&gt;, an array where an object was expected). Repairing those is fine, and I do it in a small&lt;br&gt;
normalisation pass.&lt;/p&gt;

&lt;p&gt;What I refuse to do is repair the &lt;strong&gt;user's words&lt;/strong&gt;: no keyword tables for "if / si / wenn" and no rewriting of text to&lt;br&gt;
make the model's life easier. The moment I did that, I would have reintroduced a syntax, just a hidden one. So the rule is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First choice: teach the model better, through the prompt.&lt;/li&gt;
&lt;li&gt;Second: repair the shape of its JSON output.&lt;/li&gt;
&lt;li&gt;Never: touch the wording of what the user wrote.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For what remains, there is a validator that checks the tree structurally. If it finds problems, the compiler sends them&lt;br&gt;
back to the model for correction, at most twice. If the answer is still invalid, you get a warning, and the invalid answer&lt;br&gt;
is never cached.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 4: determinism comes from caching, not from the model
&lt;/h2&gt;

&lt;p&gt;Even at temperature 0, I do not want to depend on "the model happens to answer the same way tomorrow". Answers are cached&lt;br&gt;
under a hash of (model + prompt + pseudocode), so an unchanged program never calls the model again, rebuilds are instant,&lt;br&gt;
and the same input always gives the same C#. The six sample programs even ship their cached answer, so a first build works&lt;br&gt;
without Ollama installed.&lt;/p&gt;

&lt;p&gt;If you edit the generated C# by hand, the tool keeps your edit until the pseudocode changes, then regenerates and saves the&lt;br&gt;
old file as a backup. The model is an interactive front end; the build stays boring.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does not work (yet)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A 7B model is good, not perfect. Very long or ambiguous programs sometimes need rewording.&lt;/li&gt;
&lt;li&gt;A model can be &lt;strong&gt;charitable&lt;/strong&gt;: if a student writes something slightly wrong, it may guess the intent instead of
flagging the mistake. For teaching this is a real question. I am thinking about showing the interpreted structure
back to the student so they can check it.&lt;/li&gt;
&lt;li&gt;Windows only for now, and the first generation of a new program takes seconds to minutes depending on your hardware.&lt;/li&gt;
&lt;li&gt;It is a teaching tool. I would not use it for production code, and I would not trust the model for anything with security
implications.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Is it a good idea?
&lt;/h2&gt;

&lt;p&gt;For education, I think it is worth trying: the barrier to running an idea drops, and the generated C# is a real program a&lt;br&gt;
student can open afterwards. For anything else, probably not, because a grammar is exactly the thing that makes a language&lt;br&gt;
precise and predictable. I am deliberately trading that away, and the validator, the cache and the deterministic backend&lt;br&gt;
are how I try to get some of it back.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Visual Studio extension: &lt;a href="https://marketplace.visualstudio.com/items?itemName=pseudonet-lang.pseudonet" rel="noopener noreferrer"&gt;https://marketplace.visualstudio.com/items?itemName=pseudonet-lang.pseudonet&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Without Visual Studio: &lt;code&gt;dotnet new install PseudoNet.Templates&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Source (F#, MIT): &lt;a href="https://github.com/pseudonet-lang/pseudonet" rel="noopener noreferrer"&gt;https://github.com/pseudonet-lang/pseudonet&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Site: &lt;a href="https://pseudonet-lang.github.io/" rel="noopener noreferrer"&gt;https://pseudonet-lang.github.io/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I would love to hear where it breaks, and whether you have tried a model as a &lt;em&gt;parser&lt;/em&gt; rather than a chatbot.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I used an AI assistant to help draft and edit this article and to build parts of the tool. The experiments and conclusions come from my own work on the project.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>dotnet</category>
      <category>llm</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
