<?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: Nikhilesh Kamalapurkar</title>
    <description>The latest articles on DEV Community by Nikhilesh Kamalapurkar (@nikhilesh_kamalapurkar).</description>
    <link>https://dev.to/nikhilesh_kamalapurkar</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%2F4148910%2F78fa7e2b-7a2a-43be-9887-2a3ce070cd7b.png</url>
      <title>DEV Community: Nikhilesh Kamalapurkar</title>
      <link>https://dev.to/nikhilesh_kamalapurkar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nikhilesh_kamalapurkar"/>
    <language>en</language>
    <item>
      <title>Hindsight Remembers the Exception. My Code Decides If It Applies.</title>
      <dc:creator>Nikhilesh Kamalapurkar</dc:creator>
      <pubDate>Tue, 29 Sep 2026 08:17:16 +0000</pubDate>
      <link>https://dev.to/nikhilesh_kamalapurkar/hindsight-remembers-the-exception-my-code-decides-if-it-applies-23ee</link>
      <guid>https://dev.to/nikhilesh_kamalapurkar/hindsight-remembers-the-exception-my-code-decides-if-it-applies-23ee</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%2F5fifgzykx3tctluk977p.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%2F5fifgzykx3tctluk977p.png" alt=" " width="800" height="450"&gt;&lt;/a&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%2F46t8aqvwrtfewqsuwefh.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%2F46t8aqvwrtfewqsuwefh.png" alt=" " width="800" height="418"&gt;&lt;/a&gt;&lt;br&gt;
A reviewer writes: "Use the shared transport wrapper." The author answers: "We approved an exception for this file." Nobody in the thread can say who approved it, why, for which paths, or when it stops being true.&lt;/p&gt;

&lt;p&gt;That missing context is what I built ReviewMind to keep. It is a code reviewer that remembers why an engineering exception was approved, where it applies, and when it stops applying. Its one question is: &lt;strong&gt;we approved this before, but does that approval apply here and now?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A note on status before the details. Everything runs on a synthetic TypeScript repository, and I only report what is recorded in the repo's evaluation file. Where something has not been run yet, I say so.&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%2F0rfrc6uj5a90u9gxn6s4.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%2F0rfrc6uj5a90u9gxn6s4.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  How the system hangs together
&lt;/h2&gt;

&lt;p&gt;ReviewMind has three parts that each own one job:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Supabase&lt;/strong&gt; is the authority for users, repository ownership, immutable source events, approval versions, revocation, and worker jobs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/vectorize-io/hindsight" rel="noopener noreferrer"&gt;Hindsight&lt;/a&gt;&lt;/strong&gt; is the semantic memory layer: retain, recall, and reflect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A generation model&lt;/strong&gt; (any OpenAI-compatible endpoint with JSON Schema output) explains the current change and the evidence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The piece that surprised people when I described it is what is &lt;em&gt;missing&lt;/em&gt; from that list: nothing in it lets the model decide whether an exception applies. Deterministic application code does that.&lt;/p&gt;

&lt;p&gt;The planned loop, which the repo implements end to end at the API and worker level, looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A pasted diff comes in and Hindsight recalls relevant history.&lt;/li&gt;
&lt;li&gt;Recalled facts are mapped back to canonical events. Anything I can't verify is dropped.&lt;/li&gt;
&lt;li&gt;A scoped review runs and a human gives reasoned feedback on a finding.&lt;/li&gt;
&lt;li&gt;Feedback is retained. Hindsight reflect drafts a decision from it.&lt;/li&gt;
&lt;li&gt;A maintainer approves the draft. The approved version is retained.&lt;/li&gt;
&lt;li&gt;After ingestion and a recall check, a fresh session can use it.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  The core idea: memory recalls, code decides
&lt;/h2&gt;

&lt;p&gt;The tempting design is to recall a remembered exception and ask the model whether it still holds. I didn't trust that. "Is this date before that date" and "does this glob match this path" should never be a language model's opinion.&lt;/p&gt;

&lt;p&gt;So a decision is a plain record with explicit conditions: rule key, path glob, dependency name, version range, effective time, expiry, rationale, and source event IDs. An exception is usable only when its canonical decision is approved, ready, in the same repository and run, effective at the recorded review time, unexpired, path-matched, version-matched, prerequisite-complete, and not revoked or superseded.&lt;/p&gt;

&lt;p&gt;A simplified sketch of the check (the real module is larger, this shows the shape):&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;classify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Decision&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ReviewContext&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;approved&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;               &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;needs_context&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reviewTime&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;effectiveFrom&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;expired&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expiresAt&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reviewTime&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expiresAt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;expired&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;globMatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathGlob&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;out_of_scope&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;depVersion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;needs_context&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;satisfies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;depVersion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;versionRange&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;out_of_scope&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;applicable&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The expiry boundary is exclusive: a decision applies while &lt;code&gt;effective_from &amp;lt;= review_time &amp;lt; expires_at&lt;/code&gt;. That is unit-test territory, and it stays there. The model's structured output is validated against a schema, every historical claim must carry a source ID that resolves to a real record, and any waiver the model proposes is re-checked by the decision engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Hindsight agent memory
&lt;/h2&gt;

&lt;p&gt;I could have stored exceptions in a table and searched it with keywords. I chose &lt;a href="https://hindsight.vectorize.io/" rel="noopener noreferrer"&gt;Hindsight agent memory&lt;/a&gt; because the workflow is exactly its three operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Retain&lt;/strong&gt; stores feedback and approved decision versions as immutable &lt;code&gt;event:&amp;lt;uuid&amp;gt;&lt;/code&gt; documents. Immutable IDs mean a transport retry can't quietly create a second logical event or overwrite history.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recall&lt;/strong&gt; finds semantically relevant history, including decisions worded differently from the current diff. Recalled facts must resolve back to authorized events in the current repository.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reflect&lt;/strong&gt; turns selected feedback into a structured draft decision. Reflection never approves policy. A maintainer does.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is one more rule I like: a successful retain request is not proof the memory is usable. The app tracks queued, running, retained, and ready, and "ready" requires both finished ingestion and a targeted recall that returns the expected document provenance. That follows the core idea behind &lt;a href="https://vectorize.io/what-is-agent-memory" rel="noopener noreferrer"&gt;agent memory&lt;/a&gt;: memory should carry experience across sessions, while the application keeps authority over what that experience may do.&lt;/p&gt;

&lt;h2&gt;
  
  
  One exception, three boundaries
&lt;/h2&gt;

&lt;p&gt;The example the whole project is built around is a temporary transport-wrapper exception on a legacy adapter, scoped to one path, one dependency version range, and an expiry of 30 September 2026.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On the legacy path, inside the window, the wrapper requirement is waived.&lt;/li&gt;
&lt;li&gt;On a new adapter with the same dependency version, the path doesn't match, so the exception is out of scope and the requirement stays.&lt;/li&gt;
&lt;li&gt;After expiry, the decision stays visible as history but can no longer waive anything.&lt;/li&gt;
&lt;li&gt;If a change removes the explicit 2-second request timeout while the exception is active, the timeout finding stays. Waiving the wrapper does not waive reliability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last rule is the one I care about most. A remembered "yes" must never become blanket permission. The timeout check is a small AST check, and it is intentionally fixture-scoped: it only recognizes named &lt;code&gt;request&lt;/code&gt; imports from the supplied client and helper when called with a literal options object. Aliases, spreads, computed values, and anything unfamiliar return &lt;code&gt;needs_context&lt;/code&gt;, not a pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I actually ran
&lt;/h2&gt;

&lt;p&gt;Here is what the evaluation file records, as of 28 September 2026:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;62 automated deterministic and local PostgreSQL tests passed, with 0 failures.&lt;/li&gt;
&lt;li&gt;Production typecheck and build passed, and a dependency audit reported 0 known vulnerabilities.&lt;/li&gt;
&lt;li&gt;A real-provider integration command authenticates through Supabase, retains through Hindsight, waits for the operation, recalls from a fresh client, resolves the document back to its event, and requests a validated reflection and a real structured review. It passed.&lt;/li&gt;
&lt;li&gt;A live PR-A review through the local API and worker ran with real Hindsight recall and generation. Before any approval existed, recall correctly reported no approved memory, and the wrapper convention stayed a normal finding with no invented citations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What has &lt;strong&gt;not&lt;/strong&gt; been run: the full browser journey (feedback, reflect, approve, fresh-session review), and the frozen 36-generation comparison of Generic, Static, and Memory modes across twelve held-out cases. I'm not reporting any improvement percentage because there isn't one to report yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug that taught me the most
&lt;/h2&gt;

&lt;p&gt;The first live integration run passed authentication, persistence, retention, fresh-client recall, and document resolution. Then reflection returned an HTTP 500.&lt;/p&gt;

&lt;p&gt;A simple structured reflection worked, so the failure was in my schema. The configured endpoint rejected a nullable type-array schema. When I tried an &lt;code&gt;anyOf&lt;/code&gt; variant, it returned the &lt;em&gt;string&lt;/em&gt; "null" instead of a real null and omitted the source IDs entirely.&lt;/p&gt;

&lt;p&gt;The fix was boring and worth stating plainly. The adapter now uses string wire fields for optional values, converts only empty or string-null values in designated nullable fields, and only then runs strict canonical validation. Reflection also explicitly asks for source UUIDs in its written answer so they survive structured extraction. Provider source facts are still checked independently, so an empty or invented source array does not pass.&lt;/p&gt;

&lt;p&gt;I kept the failed run's evidence file next to the passing one in the repo. The lesson is that a memory system that hands you structured drafts needs a translation layer you own, and that layer must never be allowed to weaken validation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Store the conditions, not the conclusion.&lt;/strong&gt; "Approved" without scope and expiry is a rumor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never let the model do date or path math.&lt;/strong&gt; Use tested glob and semver libraries and keep the boundary exclusive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separate team exceptions from baseline requirements.&lt;/strong&gt; The 2-second timeout sits above any wrapper waiver.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Treat retain as asynchronous.&lt;/strong&gt; Design for pending states and prove readiness with a recall probe.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep evidence inspectable.&lt;/strong&gt; For every recalled fact, show what was decided, by whom, why, where it applies, until when, and the source.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Recall can also miss. Absence from recall is not proof that no decision exists, and with no findings the reviewer says "No findings within supplied context," never "safe to merge."&lt;/p&gt;

&lt;p&gt;The code, fixtures, migrations, and evaluation record are on GitHub: &lt;a href="https://github.com/KasanagottuSnehith/ReviewMind--Code-review-with-a-memory-for-decisions-and-their-limits" rel="noopener noreferrer"&gt;ReviewMind&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Tagging &lt;a href="https://code.in/" rel="noopener noreferrer"&gt;https://code.in/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>agents</category>
    </item>
  </channel>
</rss>
