<?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: Golla Satvik Kumar</title>
    <description>The latest articles on DEV Community by Golla Satvik Kumar (@satvik8954).</description>
    <link>https://dev.to/satvik8954</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%2F4101332%2F27d8c46f-e8a1-4e4c-bb82-6c9c20d54dec.png</url>
      <title>DEV Community: Golla Satvik Kumar</title>
      <link>https://dev.to/satvik8954</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/satvik8954"/>
    <language>en</language>
    <item>
      <title>My Hindsight agent knows what the client said, and when</title>
      <dc:creator>Golla Satvik Kumar</dc:creator>
      <pubDate>Tue, 29 Sep 2026 16:41:37 +0000</pubDate>
      <link>https://dev.to/satvik8954/my-hindsight-agent-knows-what-the-client-said-and-when-3coh</link>
      <guid>https://dev.to/satvik8954/my-hindsight-agent-knows-what-the-client-said-and-when-3coh</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%2F6sv6g7qs2p41qrru4dau.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%2F6sv6g7qs2p41qrru4dau.png" alt=" " width="800" height="583"&gt;&lt;/a&gt;Ask a capable model to review a skincare ad with an icy blue background and it will tell you to push the blue further: "a deeper, more saturated blue or teal accent to reinforce a clinical feel." That's reasonable advice. It's also exactly what this client has rejected twice. Their founder's words: "No. I said this back in February: blue feels like a pharmacy. We are warm neutrals."&lt;/p&gt;

&lt;p&gt;That gap is the whole reason I built Round Zero. It's a pre-reviewer for creative agencies: before a design draft goes to a client, it predicts the feedback the client would give, and every issue it raises quotes the client's own words, with who said it, on which project, and when.&lt;/p&gt;

&lt;p&gt;The model was never the hard part. Memory was, and specifically getting memory to understand &lt;em&gt;time&lt;/em&gt;. This post is about that.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;You pick a client and upload a draft. Round Zero:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Measures the draft.&lt;/strong&gt; Colours and texture come from the pixels; layout, type and copy from a vision model (Qwen 3.8 27B on Groq).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recalls what the client has said&lt;/strong&gt; about each aspect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proposes issues&lt;/strong&gt;, each citing specific client feedback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verifies every issue&lt;/strong&gt; and resolves the citations to real quotes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When the client responds for real, their feedback goes into the same memory.&lt;/p&gt;

&lt;p&gt;The memory layer is &lt;a href="https://github.com/vectorize-io/hindsight" rel="noopener noreferrer"&gt;Hindsight, an open-source agent memory engine&lt;/a&gt;. Each client gets a memory bank with a mission and directives, which are hard rules like "no drug claims" for a skincare brand. &lt;code&gt;retain()&lt;/code&gt; stores feedback, Hindsight consolidates it into &lt;strong&gt;observations&lt;/strong&gt; (the client's taste profile), &lt;code&gt;recall()&lt;/code&gt; finds what's relevant to a draft, and &lt;code&gt;reflect()&lt;/code&gt; reasons over it with the directives applied.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;draft.png ─► palette.py (pixels)     ┐
          └► vision.py (Groq)         ├─► recall ×4 ─► reflect ─► verify ─► flags + quotes
client bank (Hindsight) ─────────────┘        ▲
client feedback ──── retain() ────────────────┘   (observations consolidate in the background)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To test it, I wrote three client histories modelled on typical agency feedback threads: a conservative law firm, a Gen-Z snack brand and a direct-to-consumer skincare brand. That's 27 projects and 91 dated client quotes, including stakeholders who contradict each other and one full rebrand.&lt;/p&gt;

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

&lt;p&gt;The snack brand, Crunchd, spent 2025 as a soft pastel brand. Its head of brand's first note on the first draft: "this is giving 2008 supermarket flyer. we're soft, not loud." Later, on the neon glitter version: "no neon. ever. it cheapens us."&lt;/p&gt;

&lt;p&gt;Then, in June 2026, they rebranded: electric orange, acid lime and hot pink on black, with chunky condensed type. The same person, in the rebrand brief: "yes, this means neon is ok now lol. i know i said never."&lt;/p&gt;

&lt;p&gt;A reviewer that remembers everything but understands nothing about time is worse than one with no memory. It would flag the new brand's drafts for using neon. I wanted one pastel draft to come back &lt;strong&gt;ready for client&lt;/strong&gt; against the April 2026 memory and &lt;strong&gt;flagged&lt;/strong&gt; against the September one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Retain in order, with real timestamps
&lt;/h3&gt;

&lt;p&gt;Memory has to be written the way it happened. The seed script retains every quote in chronological order with its original timestamp, and after each project it polls Hindsight's Operations API until consolidation finishes. Each quote is its own document, with author, role, project and date as metadata.&lt;/p&gt;

&lt;h3&gt;
  
  
  Then the rebrand didn't land
&lt;/h3&gt;

&lt;p&gt;After the first full seed, I looked at the after-rebrand bank's observations. The new palette was in there. So was "Neon colors are prohibited in all brand designs", untouched. So was "brand identity is defined as soft rather than loud". Hindsight hadn't updated the old beliefs. It had added new ones next to them.&lt;/p&gt;

&lt;p&gt;The cause was my own tagging. I tag every memory with its client, project and phase so I can filter recall later. Hindsight's default observation scope is &lt;em&gt;combined&lt;/em&gt;: a memory only consolidates with memories that carry &lt;strong&gt;exactly the same tag set&lt;/strong&gt;. By tagging per project, I had split each client into one isolated taste profile per project. The rebrand feedback lived in the &lt;code&gt;project:cr-08&lt;/code&gt; island and never met the neon rule from &lt;code&gt;project:cr-05&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://hindsight.vectorize.io/" rel="noopener noreferrer"&gt;Hindsight docs on retain&lt;/a&gt; cover the alternatives: &lt;code&gt;per_tag&lt;/code&gt;, &lt;code&gt;shared&lt;/code&gt;, &lt;code&gt;all_combinations&lt;/code&gt;, or an explicit list. I set the scope explicitly, per memory:&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="nd"&gt;@property&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;observation_scopes&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="o"&gt;-&amp;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;list&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="c1"&gt;# Hindsight's default ("combined") consolidates only among memories with the exact
&lt;/span&gt;    &lt;span class="c1"&gt;# same tag set, which would silo every project. All of a client's feedback must feed
&lt;/span&gt;    &lt;span class="c1"&gt;# one taste profile, so observations are scoped to the client tag alone. Project and
&lt;/span&gt;    &lt;span class="c1"&gt;# phase tags stay on the facts for recall filtering.
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&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;client:&lt;/span&gt;&lt;span class="si"&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;client_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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;retain()&lt;/code&gt; doesn't take that parameter in the Python SDK, but &lt;code&gt;retain_batch()&lt;/code&gt; does, so every write goes through one helper:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;retain_item&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;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MemoryItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# retain_batch rather than retain(): only the batch form accepts observation_scopes.
&lt;/span&gt;    &lt;span class="nf"&gt;hindsight&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;retain_batch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;bank_id&lt;/span&gt;&lt;span class="o"&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;items&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;context&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;document_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;document_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;metadata&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tags&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;observation_scopes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;observation_scopes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Proof counts jumped straight away. Once it could draw on every project, the law firm's palette observation was backed by &lt;strong&gt;17&lt;/strong&gt; pieces of feedback.&lt;/p&gt;

&lt;h3&gt;
  
  
  Then it over-corrected
&lt;/h3&gt;

&lt;p&gt;With everything in one scope, the after-rebrand bank collapsed to &lt;strong&gt;5&lt;/strong&gt; observations. My observations mission had said "prefer one observation per rule", so Hindsight built a catch-all "soft aesthetic" observation holding lowercase type, grain texture and a ban on "ALERT". When the rebrand contradicted part of it, consolidation replaced the whole thing, and rules the rebrand never touched disappeared with it.&lt;/p&gt;

&lt;p&gt;The fix was in the mission text: exactly one topic per observation (&lt;code&gt;[Colour]&lt;/code&gt;, &lt;code&gt;[Typography]&lt;/code&gt;, &lt;code&gt;[Copy]&lt;/code&gt;…), and update a reversed rule in place rather than delete it. After re-seeding, untouched rules survived, and the &lt;code&gt;[Compliance]&lt;/code&gt; observation's history now shows the founder's April worry about "becoming too generic" turning into "adopted a LOUD identity". The colour observation was still replaced rather than updated, so the history doesn't link back to the pastel version. I stopped tuning there: the demo compares snapshot banks from before and after the rebrand, which Hindsight's &lt;code&gt;clone_bank&lt;/code&gt; makes cheap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making reflect show its receipts
&lt;/h2&gt;

&lt;p&gt;For the review itself, I recall once per draft aspect (colour, typography, layout and imagery, copy) and number every returned quote in date order: F1 through F36, oldest first. Then I call &lt;code&gt;reflect()&lt;/code&gt; with a response schema that requires evidence IDs on every flag:&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;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;hindsight&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;reflect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;bank_id&lt;/span&gt;&lt;span class="o"&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="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;# the draft description and the review rules
&lt;/span&gt;    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;# numbered, dated client quotes: "[F12] 2025-10-09 · Amara Nwosu (Founder) · ..."
&lt;/span&gt;    &lt;span class="n"&gt;budget&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;response_schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;REVIEW_JSON_SCHEMA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;include_facts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;apply_all_directives&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&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;Because each quote was retained as its own document, resolving &lt;code&gt;F12&lt;/code&gt; back to "Amara Nwosu, Founder, winter barrier kit banner, 9 Oct 2025" is a dictionary lookup, not a guess.&lt;/p&gt;

&lt;p&gt;I didn't expect to need a second model after reflect. But in one run, reflect produced four correct flags with empty evidence arrays; my resolver dropped them, and a draft that should have been rejected came back "ready for client". It also sometimes applied a photo-grading rule to a vector illustration. Now a verifier (Groq's &lt;code&gt;gpt-oss-120b&lt;/code&gt;, temperature 0) checks each flag: is the problem really in the draft, is the instruction still current, does it apply to this kind of asset? It also fills in missing evidence IDs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does now
&lt;/h2&gt;

&lt;p&gt;On the rebrand draft:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Before-rebrand memory:&lt;/strong&gt; ready for client. It matches the pattern the client approved in March 2025.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;After-rebrand memory:&lt;/strong&gt; three high-severity flags (palette, grain, type), each citing feedback from after the rebrand, such as the founder's "Drop the grain and the vintage grade. Crisp, punchy, high contrast. The faded look is the old brand." (18 June 2026)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Without memory:&lt;/strong&gt; the same "make it bolder" advice at both dates, so it's wrong at one of them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the skincare draft, with eight problems planted in it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Without memory:&lt;/strong&gt; 2 of 8, plus the advice to make it more clinical.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;With memory:&lt;/strong&gt; 6, 8, 6 and 7 of 8 across four runs of the final version.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every run caught&lt;/strong&gt; the drug claim, the before/after split, the front-on bottle, the missing ingredient percentages and the gradients.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weak spot:&lt;/strong&gt; "Clinically proven" without a cited study was caught in only 1 of 7 runs, even though a directive names that phrase. Those numbers are in the README; a reviewer that is sometimes wrong should say so.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I'd tell you before you build this
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Tags are consolidation boundaries, not just filters.&lt;/strong&gt; Decide explicitly what observations should consolidate across. &lt;code&gt;observation_scopes&lt;/code&gt; makes that one line, once you know the default scopes by the full tag set.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Write memory in the order it happened.&lt;/strong&gt; Backdate timestamps and let consolidation finish between batches. A reversal only reads as a reversal if the thing it reverses is already there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. One quote per document gives you citations for free.&lt;/strong&gt; Metadata on each retained item lets every flag show the client's exact words and date. That's the difference between an opinion and evidence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Don't make the model do what code can.&lt;/strong&gt; The vision model called a peach background "white". Colours and texture are measured from pixels now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Measure across runs.&lt;/strong&gt; Reflect proposes slightly different issues each time, so I publish the real catch rate and record reviews for replay when a live run misbehaves.&lt;/p&gt;

&lt;p&gt;If you're wondering &lt;a href="https://vectorize.io/what-is-agent-memory" rel="noopener noreferrer"&gt;what agent memory actually is&lt;/a&gt; beyond "a vector store with a nicer name", this project changed my answer. Storage was easy. The work was deciding what counts as the same thing, what order things happened in, and what the agent may forget.&lt;/p&gt;

&lt;p&gt;The code is on GitHub: &lt;a href="https://github.com/satvik8954/RoundZero" rel="noopener noreferrer"&gt;Round Zero&lt;/a&gt;. It uses &lt;a href="https://github.com/vectorize-io/hindsight" rel="noopener noreferrer"&gt;Hindsight&lt;/a&gt; for memory, with Groq for vision and verification, FastAPI with server-sent events for streaming progress, and a Next.js front end.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building Data Watchdog: A Two-Gate Approval Agent on TrueForge</title>
      <dc:creator>Golla Satvik Kumar</dc:creator>
      <pubDate>Sun, 30 Aug 2026 11:51:38 +0000</pubDate>
      <link>https://dev.to/satvik8954/building-data-watchdog-a-two-gate-approval-agent-on-trueforge-3aoe</link>
      <guid>https://dev.to/satvik8954/building-data-watchdog-a-two-gate-approval-agent-on-trueforge-3aoe</guid>
      <description>&lt;p&gt;I went into the WeMakeDevs Agent Harness Hackathon wanting to build something using TrueForge's approval-gate system properly — not just have a model promise to "ask before doing things," but actually have the harness enforce that pause. Here's how it went, dead ends included.&lt;/p&gt;

&lt;p&gt;Starting point: the Approval-Gated Assistant&lt;/p&gt;

&lt;p&gt;The hackathon's example ideas list a simple starting point: an agent that drafts something (an email, a ticket) and pauses before doing anything irreversible. I picked Gmail as the connected tool, since I already had an account.&lt;/p&gt;

&lt;p&gt;That turned out to be the first wall. Gmail's MCP server requires a Google Cloud OAuth Client set up on the developer side, and even after wiring up the OAuth consent screen, scopes, and redirect URI, TrueForge's connector kept failing with a DCR (Dynamic Client Registration) error — Gmail's MCP endpoint doesn't support DCR, and TrueForge's UI only offered sign-in via DCR, not manual Client ID/Secret entry. After a few rounds of trying to force it, I switched to GitHub instead — a built-in catalog connector, header-auth based, and much simpler to wire up with a personal access token.&lt;/p&gt;

&lt;p&gt;Getting the approval gate to actually work&lt;/p&gt;

&lt;p&gt;The UI doesn't expose a "require approval" toggle per tool — that setting only exists at the API level, in the agent manifest:&lt;/p&gt;

&lt;p&gt;json&lt;br&gt;
"mcp_servers": [&lt;br&gt;
  {&lt;br&gt;
    "name": "github",&lt;br&gt;
    "enable_tools": ["@all"],&lt;br&gt;
    "require_approval_for_tools": ["issue_write"]&lt;br&gt;
  }&lt;br&gt;
]&lt;/p&gt;

&lt;p&gt;Once I set this via a direct PUT /api/v1/agents/ call, the harness genuinely paused before creating a GitHub issue — showing the raw tool-call request and an Allow/Deny button. That was the moment the project started feeling like a real approval-gated agent rather than a polite prompt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wanting more: trying an Analytics Agent&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With the certificate basically secured, I decided to push for an actual competitive submission and picked the Analytics Agent idea — natural language question → SQL → execution → explanation.&lt;/p&gt;

&lt;p&gt;This meant enabling TrueForge's sandbox, which currently only supports Daytona as a provider. I hit another wall here: Daytona rejected my API key from inside TrueForge, even though the same credentials worked fine locally on my own machine — never fully resolved why.&lt;/p&gt;

&lt;p&gt;Rather than keep debugging an external dependency close to deadline, I built a small custom MCP server in Python (using the mcp SDK) that exposes a run_sql tool directly over a local SQLite database (the Chinook sample dataset — a music store with customers, invoices, and genres). This sidestepped Daytona entirely, reused the same header-auth-free custom-connector pattern TrueForge supports, and got me a working SQL tool in under 20 minutes once the direction was clear.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The idea that tied it together: Data Watchdog&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Rather than treat the GitHub agent and the analytics agent as two separate, generic submissions, I combined them into one agent with two independent approval gates and real branching logic:&lt;/p&gt;

&lt;p&gt;Agent proposes a SQL query against the database → pauses for approval → runs it&lt;br&gt;
Agent evaluates the result against a stated rule (flag if a category's revenue is more than 30% below the average)&lt;br&gt;
If the threshold is met: agent drafts a GitHub issue with the finding as evidence → pauses for approval again → creates it&lt;br&gt;
If not: agent reports the finding in chat and stops — no second approval, no issue&lt;/p&gt;

&lt;p&gt;I tested both branches explicitly. Asking about the lowest-revenue genre in the Chinook database correctly triggered both approval gates and created a real GitHub issue with the underlying numbers as evidence. Asking about the highest-revenue genre correctly reasoned through the same threshold and concluded there was nothing worth flagging — no issue created, no second gate triggered. That branching is what convinced me this was a genuine decision point, not a fixed two-step pipeline dressed up as one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code quality with Qodo&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I connected Qodo to the GitHub repo early and ran every meaningful change through a pull request rather than pushing straight to main. Qodo reviewed each PR automatically — checking for bugs, rule violations, and requirement gaps — and reported no issues on the changes that shipped.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I'd do differently&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If I'd picked GitHub as my connector from the very start instead of chasing Gmail's OAuth flow, I'd have had a lot more runway to build something more ambitious with the sandbox itself. The lesson that stuck with me: TrueForge's built-in catalog connectors (GitHub, Linear, etc.) are dramatically less friction than anything requiring custom OAuth setup, and that's worth weighing heavily when time is tight.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;br&gt;
Repo: github.com/satvik8954/Wemakedevs&lt;br&gt;
Qodo-reviewed PRs: #1, #2 (Data Watchdog)&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>buildinpublic</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
