<?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: Avinash Gehi</title>
    <description>The latest articles on DEV Community by Avinash Gehi (@avinash_gehi30).</description>
    <link>https://dev.to/avinash_gehi30</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%2F3984246%2F85c1efac-434e-4539-88e0-48dfd7c7a847.png</url>
      <title>DEV Community: Avinash Gehi</title>
      <link>https://dev.to/avinash_gehi30</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/avinash_gehi30"/>
    <language>en</language>
    <item>
      <title>I ported croniter to Rust and got 228/228. That number proved nothing</title>
      <dc:creator>Avinash Gehi</dc:creator>
      <pubDate>Fri, 07 Aug 2026 13:08:34 +0000</pubDate>
      <link>https://dev.to/avinash_gehi30/i-ported-croniter-to-rust-and-got-228228-that-number-proved-nothing-3lgd</link>
      <guid>https://dev.to/avinash_gehi30/i-ported-croniter-to-rust-and-got-228228-that-number-proved-nothing-3lgd</guid>
      <description>&lt;p&gt;A port that compiles and passes its tests isn't evidence the port is correct. It's evidence that whoever wrote the port also controlled the tests. Generating a port is nearly free now. Proving it holds up is the part almost nobody does.&lt;/p&gt;

&lt;p&gt;So here's what I actually did to try to falsify my own &lt;a href="https://github.com/pallets-eco/croniter" rel="noopener noreferrer"&gt;croniter&lt;/a&gt; → Rust port, and where it fell short.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Run the &lt;em&gt;original&lt;/em&gt; tests, hash-pinned
&lt;/h2&gt;

&lt;p&gt;Not a translated suite. The actual upstream files, SHA-256 fingerprinted before a line of Rust existed, wired to Rust through a PyO3 bridge. &lt;code&gt;git log -- tests/original/&lt;/code&gt; shows one commit: the vendoring.&lt;/p&gt;

&lt;p&gt;Then the step I'd argue is mandatory: &lt;strong&gt;build the bridge against a deliberately wrong stub first and confirm the tests fail.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;222 failed, 6 passed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's success — it proves the suite is importing and judging Rust before any correct logic exists to muddy the signal. And note the 6: my stub returned &lt;code&gt;False&lt;/code&gt; from &lt;code&gt;is_valid&lt;/code&gt;, which satisfies every test asserting an expression is invalid. Even my broken baseline had false positives.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Sabotage your own green suite
&lt;/h2&gt;

&lt;p&gt;228/228 has two explanations and you can't tell them apart from the green: the port is right, or the suite can't fail. So I broke it on purpose — two single-token changes in two unrelated files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;consts.rs   hour range (0,23) -&amp;gt; (0,22)  -&amp;gt;  32 failed, 196 passed
expand.rs   wrap length +1 -&amp;gt; +2          -&amp;gt;   2 failed, 226 passed
reverted                                  -&amp;gt; 228 passed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ten minutes, and it's the difference between a measurement and a decoration. I did the same to the benchmark checksum.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Make the library contradict itself
&lt;/h2&gt;

&lt;p&gt;The technique I'd steal from this project. croniter exposes three APIs answering overlapping questions — &lt;code&gt;get_next&lt;/code&gt;, &lt;code&gt;get_prev&lt;/code&gt;, &lt;code&gt;match&lt;/code&gt; — and they &lt;em&gt;must&lt;/em&gt; agree. If &lt;code&gt;get_next(start)&lt;/code&gt; returns &lt;code&gt;N&lt;/code&gt;, nothing strictly between may &lt;code&gt;match&lt;/code&gt;, and &lt;code&gt;N&lt;/code&gt; must &lt;code&gt;match&lt;/code&gt;. A violation means the library contradicts itself and one answer is wrong under any reading of cron semantics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's an oracle with no external reference.&lt;/strong&gt; No second implementation, no spec, no human. The library grades itself.&lt;/p&gt;

&lt;p&gt;My first one was worthless. It checked one property, on naive datetimes only, never called &lt;code&gt;get_prev&lt;/code&gt;. 19,440 cases, zero findings — and I briefly read zero as correctness. It was evidence the question was too easy. &lt;strong&gt;An invariant that can't fail isn't an oracle.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The rewrite checked five properties, called &lt;code&gt;get_prev&lt;/code&gt;, and biased half its start times to within four hours of a real DST transition — including Australia/Lord_Howe, the only zone on Earth with a 30-minute shift. It found two real bugs in croniter, both now filed upstream (&lt;a href="https://github.com/pallets-eco/croniter/issues/258" rel="noopener noreferrer"&gt;#258&lt;/a&gt;, &lt;a href="https://github.com/pallets-eco/croniter/issues/259" rel="noopener noreferrer"&gt;#259&lt;/a&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="n"&gt;tz&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zoneinfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ZoneInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Australia/Lord_Howe&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;43&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tzinfo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;croniter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0 * * * *&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;get_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# 03:00+11:00
&lt;/span&gt;&lt;span class="nf"&gt;croniter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0 * * * *&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nxt&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;get_prev&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;# 02:30+11:00  &amp;lt;- AFTER start
&lt;/span&gt;&lt;span class="n"&gt;croniter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0 * * * *&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;02&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="c1"&gt;# True
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;match&lt;/code&gt; returns &lt;code&gt;True&lt;/code&gt; for a &lt;strong&gt;minute-0&lt;/strong&gt; schedule at &lt;strong&gt;minute 30&lt;/strong&gt;. In a normal 1-hour zone the same code path lands on 03:00, which &lt;em&gt;is&lt;/em&gt; valid — so the bug is invisible everywhere except the 30-minute shift. Which is exactly why the generator was pointed there.&lt;/p&gt;

&lt;p&gt;The second: &lt;code&gt;croniter_range&lt;/code&gt;'s stop test is &lt;code&gt;v &amp;lt; stop&lt;/code&gt;, and CPython ignores &lt;code&gt;tzinfo&lt;/code&gt; when both operands share it. Across a DST transition it compares wall-clock instead of elapsed time — returning 1 result where 6 exist, or results outside the interval you asked for. Silent, no exception.&lt;/p&gt;

&lt;p&gt;My port reproduces both deliberately. A port's job is to behave like the thing it ports, including where that's wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The bug 228 passing tests could not find
&lt;/h2&gt;

&lt;p&gt;Differential fuzzing: same probe under two interpreters, comparing values &lt;em&gt;and exception types&lt;/em&gt;. Adding timezone-aware inputs surfaced &lt;strong&gt;221 divergences in 164,500&lt;/strong&gt; — all one cause, and it was a type, not a value. croniter raises a bare &lt;code&gt;ValueError&lt;/code&gt;; my port raised &lt;code&gt;CroniterError&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CroniterError&lt;/code&gt; subclasses &lt;code&gt;ValueError&lt;/code&gt;. Every &lt;code&gt;except ValueError&lt;/code&gt; caught it. &lt;strong&gt;The suite was green at 228/228 before and after.&lt;/strong&gt; It could not have found this, no matter how long I ran it.&lt;/p&gt;

&lt;p&gt;That's the whole argument for differential fuzzing in one paragraph. Zero value divergences, though — the date math was right, only a label was wrong. Final run: 160,500 inputs, 0 divergences.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. What I'd take back
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The first oracle cost a day and taught nothing.&lt;/strong&gt; I should have asked "what input would falsify this?" &lt;em&gt;before&lt;/em&gt; running it for an hour.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Triage cost more than the hunt.&lt;/strong&gt; One harness gave 927 raw findings; 750 were documented behaviour. An earlier one gave 1,408 findings that were entirely my own bug in the checker.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The fuzzer tests the bridge, not the shipped binary.&lt;/strong&gt; Both sides run under Python, so &lt;code&gt;core&lt;/code&gt; is validated &lt;em&gt;as called through PyO3&lt;/em&gt;. The artifact judges receive is one layer removed from the evidence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;228/228 hid a hole in the deliverable.&lt;/strong&gt; Every timezone test supplies a &lt;code&gt;tzinfo&lt;/code&gt;, so they all went through the bridge — while the standalone binary couldn't do DST at all. A suite measures the path the tests take. Mine bypassed a third of the product and reported full marks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I wrote an unverified claim into my own README&lt;/strong&gt; (a Docker build that had never run). Caught it late, marked it unverified rather than deleting it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The number nobody prints:&lt;/strong&gt; the suite runs in 1.54s against Python and ~1.8s against my 25x-faster Rust. Every call crosses FFI. Both facts are true at once.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Numbers
&lt;/h2&gt;

&lt;p&gt;228/228 on unmodified tests · 160,500 fuzz inputs, 0 divergences · 2 upstream bugs filed · 0 &lt;code&gt;unsafe&lt;/code&gt; (compiler-enforced) · 25.3x mean, 26.1x p99, 3.2x smaller RSS · 0 test files modified.&lt;/p&gt;

&lt;p&gt;Every figure was observed on one machine and written down after the run. The one claim I couldn't verify is marked as unverified in the repo rather than dropped.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/Avi36005/Portmortem-Team-Kryptonite" rel="noopener noreferrer"&gt;github.com/Avi36005/Portmortem-Team-Kryptonite&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
      <category>python</category>
    </item>
    <item>
      <title>Why stateless LLMs can't make consistent decisions, and how Hindsight fixed that</title>
      <dc:creator>Avinash Gehi</dc:creator>
      <pubDate>Mon, 15 Jun 2026 12:17:45 +0000</pubDate>
      <link>https://dev.to/avinash_gehi30/why-stateless-llms-cant-make-consistent-decisions-and-how-hindsight-fixed-that-8nn</link>
      <guid>https://dev.to/avinash_gehi30/why-stateless-llms-cant-make-consistent-decisions-and-how-hindsight-fixed-that-8nn</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.amazonaws.com%2Fuploads%2Farticles%2Fd3ioz0c4i1ye2ihp3jot.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.amazonaws.com%2Fuploads%2Farticles%2Fd3ioz0c4i1ye2ihp3jot.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Fc0a3b82pjd528kka1wo5.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.amazonaws.com%2Fuploads%2Farticles%2Fc0a3b82pjd528kka1wo5.png" alt=" " width="800" height="439"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Fs41f1u5ynjiao1imtwby.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.amazonaws.com%2Fuploads%2Farticles%2Fs41f1u5ynjiao1imtwby.png" alt=" " width="800" height="396"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2F1rksh2b38pfl67x6j5zb.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.amazonaws.com%2Fuploads%2Farticles%2F1rksh2b38pfl67x6j5zb.png" alt=" " width="800" height="345"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2F97qo58m33956r7t0ep2p.jpeg" 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.amazonaws.com%2Fuploads%2Farticles%2F97qo58m33956r7t0ep2p.jpeg" alt=" " width="800" height="520"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2F5vreincbvwppzclwp8ta.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.amazonaws.com%2Fuploads%2Farticles%2F5vreincbvwppzclwp8ta.png" alt=" " width="800" height="347"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Fr1t39tzxhd5b5j7sr2fd.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.amazonaws.com%2Fuploads%2Farticles%2Fr1t39tzxhd5b5j7sr2fd.png" alt=" " width="800" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/qV4Go-1dJIA"&gt;
  &lt;/iframe&gt;
I spend most of my time in the frontend. I build dashboards, wire up routes, push the thing to a CDN, and obsess over the half-second between a click and something useful appearing on screen. So when we set out to build &lt;a href="https://github.com/Avi36005/ExceptionOS" rel="noopener noreferrer"&gt;ExceptionOS&lt;/a&gt; — a platform that helps companies make consistent, explainable decisions about business exceptions like refunds, discount approvals, and SLA compensation — the part I owned was the surface: the React app, the deploy pipeline, and a chat-plus-voice assistant that anyone could talk to.&lt;/p&gt;

&lt;p&gt;The interesting problem turned out not to be the UI at all. It was what sat behind it: a memory layer that remembers every decision an organization has ever made, and an assistant that answers questions by recalling from it. This is the story of building that assistant, the dumb mistake I made that made it feel slow and weird, and how a memory system called &lt;a href="https://hindsight.vectorize.io/" rel="noopener noreferrer"&gt;Hindsight&lt;/a&gt; ended up shaping the whole product.&lt;/p&gt;
&lt;h2&gt;
  
  
  What the system actually does
&lt;/h2&gt;

&lt;p&gt;ExceptionOS captures a business exception — say, a customer asking for a refund outside policy — and runs it through a debate. Ten specialized agents look at the case from different angles: one finds the applicable policy, one estimates the financial hit, one assesses churn risk, one digs up similar past cases, and one plays critic and pokes holes in the emerging recommendation. The output is a structured recommendation with reasoning a human can read and override.&lt;/p&gt;

&lt;p&gt;That debate is only as good as its memory. An agent that finds "similar past cases" needs somewhere those cases live. We use Hindsight Cloud for that — &lt;a href="https://vectorize.io/what-is-agent-memory" rel="noopener noreferrer"&gt;agent memory&lt;/a&gt; as a managed service, with three operations we lean on constantly: retain (store a decision), recall (find relevant ones), and reflect (surface patterns over time). Every organization gets its own memory bank, created on first use:&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;bank&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hindsight&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_bank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exceptionos-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;org_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;organization_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Memory bank for organisation &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;org_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; on ExceptionOS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One bank per org means recall is naturally scoped — Acme's assistant never sees Globex's decisions. That property mattered a lot once I started building the front-facing assistant.&lt;/p&gt;

&lt;h2&gt;
  
  
  The assistant: a chat orb that knows your history
&lt;/h2&gt;

&lt;p&gt;The feature I'm proudest of is a floating orb that lives in the corner of every screen. You can type at it or talk to it. Ask "what's our approval rate for contractor exceptions?" and it answers from your organization's actual decision history, then reads the answer aloud.&lt;/p&gt;

&lt;p&gt;The frontend side is deliberately thin. The browser doesn't talk to the memory layer or the LLM directly — it posts a message, an optional bank ID, and the current page context to one backend endpoint, and gets back an answer plus the sources it used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;askAssistant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;bankId&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;context&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&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="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AssistantReply&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;API&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/v1/assistant/chat`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;authHeader&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;bank_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bankId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sources&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Passing &lt;code&gt;context&lt;/code&gt; — the case or page the user is currently looking at — is what makes the assistant feel like it's &lt;em&gt;there with you&lt;/em&gt;. Ask "is this one risky?" while staring at a specific case and it knows what "this" means. Passing &lt;code&gt;bankId&lt;/code&gt; is what makes it org-aware. Two small fields, most of the perceived intelligence.&lt;/p&gt;

&lt;p&gt;On the backend, the endpoint recalls grounding memories from Hindsight and feeds them to the LLM as context. The recall itself is one HTTP call:&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;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;recall_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bank_id&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;query&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;top_k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hindsight&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bank_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top_k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;top_k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then a system prompt tells the model to use those memories only when the question actually calls for them, answer in one to three sentences, and keep a natural spoken tone — because that same text gets sent to &lt;a href="https://github.com/Avi36005/ExceptionOS" rel="noopener noreferrer"&gt;ElevenLabs&lt;/a&gt; for voice synthesis and read back to the user.&lt;/p&gt;

&lt;h2&gt;
  
  
  The mistake: recalling on "hi"
&lt;/h2&gt;

&lt;p&gt;Here's where I got it wrong. My first version was clean and uniform: every message went through the same path. User says something, we recall from memory, we hand the memories to the model, we answer. Symmetry felt right.&lt;/p&gt;

&lt;p&gt;It was terrible.&lt;/p&gt;

&lt;p&gt;You'd open the orb, type "hi", and wait. Behind that one word the system was doing a full vector recall against the org's entire decision history, pulling five "relevant" memories about refunds and NDAs, and stuffing them into the prompt. The model, dutifully handed a pile of past cases, would respond to "hi" by &lt;em&gt;listing refund precedents&lt;/em&gt;. It was slow — a network round-trip to the memory layer before any greeting — and it was unsettling, like saying hello to someone who immediately recites your file.&lt;/p&gt;

&lt;p&gt;The fix was to admit that not every message is a query. Small talk shouldn't touch memory at all:&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;normalized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&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="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&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="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;is_smalltalk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;normalized&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;GREETINGS&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;

&lt;span class="n"&gt;memories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&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="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;is_smalltalk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;memories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nc"&gt;RecallService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get_hindsight_client&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;recall_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bank_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top_k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt; "hi" → recall five memories → 2-second pause → an awkward dump of past refund cases.&lt;br&gt;
&lt;strong&gt;After:&lt;/strong&gt; "hi" → no recall → instant, warm one-liner inviting you to ask about a case.&lt;/p&gt;

&lt;p&gt;The system prompt reinforces it: greetings get a warm sentence and never enumerate cases; memories get referenced only when the user asks about a case, refund, discount, policy, or decision. The lesson generalizes well beyond greetings. Recall is not free — it costs a round-trip and it costs prompt space — and a memory system is most impressive when it stays quiet until it has something worth saying.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gotcha that cost me an afternoon: metadata is strings only
&lt;/h2&gt;

&lt;p&gt;A quieter lesson lived at the boundary between our data model and Hindsight's. Hindsight's memory metadata accepts string values only, so anything structured — case IDs, financial figures, nested objects — has to be coerced or JSON-encoded before a retain call, or it silently fails to stick. The fix was a small normalizer that every retain passes through. The takeaway: when you adopt a managed memory layer, learn its type contract early — the constraints are usually there for good reasons, and guessing from the client side just wastes an afternoon.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell someone starting this
&lt;/h2&gt;

&lt;p&gt;Shipping the frontend taught me that a memory layer doesn't live in the backend — it leaks into every product decision you make. Whether to recall, when to recall, how much to show, what to read aloud: those are UX calls as much as infrastructure calls. Two small request fields (&lt;code&gt;bank_id&lt;/code&gt;, &lt;code&gt;context&lt;/code&gt;) carried most of the assistant's apparent intelligence. One conditional (&lt;code&gt;is_smalltalk&lt;/code&gt;) carried most of its perceived speed and warmth.&lt;/p&gt;

&lt;p&gt;If you're building something similar, start with the operations — retain, recall, reflect — and resist the urge to apply them uniformly. The product feels smart not when it remembers everything, but when it knows the difference between a question and a hello.&lt;/p&gt;

&lt;p&gt;You can see the project at &lt;a href="https://github.com/Avi36005/ExceptionOS" rel="noopener noreferrer"&gt;github.com/Avi36005/ExceptionOS&lt;/a&gt;, read more about the memory layer in the &lt;a href="https://hindsight.vectorize.io/" rel="noopener noreferrer"&gt;Hindsight docs&lt;/a&gt; and its &lt;a href="https://github.com/vectorize-io/hindsight" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;, or dig into the concept of &lt;a href="https://vectorize.io/what-is-agent-memory" rel="noopener noreferrer"&gt;agent memory&lt;/a&gt; itself.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>programming</category>
      <category>python</category>
    </item>
  </channel>
</rss>
