<?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: Vinayak Sonthalia</title>
    <description>The latest articles on DEV Community by Vinayak Sonthalia (@vinayaksonthalia).</description>
    <link>https://dev.to/vinayaksonthalia</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%2F4016272%2F06216a7a-b34b-4a55-8064-d54ff1f51642.jpg</url>
      <title>DEV Community: Vinayak Sonthalia</title>
      <link>https://dev.to/vinayaksonthalia</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vinayaksonthalia"/>
    <language>en</language>
    <item>
      <title>Building a Postman Connector for Cognee: What I Learned About Safer Syncs</title>
      <dc:creator>Vinayak Sonthalia</dc:creator>
      <pubDate>Sat, 31 Oct 2026 18:35:58 +0000</pubDate>
      <link>https://dev.to/vinayaksonthalia/building-a-postman-connector-for-cognee-what-i-learned-about-safer-syncs-12d9</link>
      <guid>https://dev.to/vinayaksonthalia/building-a-postman-connector-for-cognee-what-i-learned-about-safer-syncs-12d9</guid>
      <description>&lt;h1&gt;
  
  
  A Postman sync that knows when to stop
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;What repeated review taught me about snapshots, deletion safety, privacy, and proving a Cognee connector works end to end.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A Postman request changed from &lt;strong&gt;“Aster lives in Oslo”&lt;/strong&gt; to &lt;strong&gt;“Aster lives in Lima.”&lt;/strong&gt; After syncing the edit into a local Cognee setup, a search found Lima; checks of the stored content found no remaining Oslo text. When I deleted the synthetic request, the corresponding Data, graph, and vector records were empty too.&lt;/p&gt;

&lt;p&gt;That small lifecycle captures the difficult part of building a connector: getting fresh content in is easy to demonstrate. Getting changes and removals right, without deleting unrelated data or leaking secrets, takes more work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The sync is a snapshot, not an append
&lt;/h2&gt;

&lt;p&gt;A connector that only adds documents can leave old facts behind. The Postman source therefore emits a complete snapshot for its own stable resource identity. Unchanged documents are retained from cache, while changed and removed documents flow through Cognee’s replacement path.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    A[Validate account and workspace] --&amp;gt; B[Read collection inventory]
    B --&amp;gt; C{Inventory complete and stable?}
    C -- No --&amp;gt; X[Stop; keep prior snapshot]
    C -- Yes --&amp;gt; D[Fetch changed trees; reuse unchanged cached rows]
    D --&amp;gt; E[Build complete retained snapshot]
    E --&amp;gt; F{Removal is ambiguous or exceeds guard?}
    F -- Yes --&amp;gt; Y[Stop and report UIDs for explicit confirmation]
    F -- No --&amp;gt; G[Publish scoped replacement to Cognee]
    G --&amp;gt; H[Verify Data, graph, vectors, and search]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;This design keeps unchanged content in the snapshot instead of confusing “not fetched this time” with “deleted.” Resource names provide isolation between sibling sources. A deliberate selection change can retire content from one resource without wiping another.&lt;/p&gt;

&lt;p&gt;Deletion is where an ordinary inventory comparison becomes dangerous. A partial listing caused by access changes can resemble mass deletion. The connector waits for matching inventory passes before trusting changed listings, refuses to clear a populated cache from an empty inventory without confirmation, and keeps pending removals across runs. Pending entries do not vanish just because a scheduled sync was delayed.&lt;/p&gt;

&lt;p&gt;These checks reduce accidental loss; they do not reveal why Postman stopped showing a collection. It may have been deleted, moved, unshared, or hidden by permissions. The documented removal policy makes that tradeoff explicit and allows callers to require confirmation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Privacy is a boundary, not a regex promise
&lt;/h2&gt;

&lt;p&gt;A collection can place credentials in auth settings, variables, query parameters, custom headers, URL paths, or request bodies. Early review rounds exposed both sides of the problem: redaction that ate ordinary words, and secrets that still appeared in generated prose.&lt;/p&gt;

&lt;p&gt;Each reported example became a regression case. The current filtering covers known sensitive fields and credential patterns, including secret variables, sensitive body fields, custom auth headers, query values, long token-like strings, hex tokens, JWT shapes, and known path patterns. Raw request trees are not persisted, and auth, scripts, examples, and raw values are omitted from rendered documents.&lt;/p&gt;

&lt;p&gt;That is &lt;strong&gt;best-effort filtering&lt;/strong&gt;, not universal secret detection. A short value can be indistinguishable from a normal word; an unusual credential can evade known patterns. The documentation says so. Users should review source collections and provide explicit values to redact where needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review comments became executable cases
&lt;/h2&gt;

&lt;p&gt;The PR went through several rounds of detailed review. Feedback covered false deletion, inventory gaps, documentation-only collections, redaction, request URLs, retry limits, unnecessary API calls, and error reporting. The useful shift was to stop treating each comment as a local patch and instead ask: &lt;em&gt;what small test would fail if this happens again?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The latest recorded offline suite reports &lt;strong&gt;185 passed and 1 skipped&lt;/strong&gt;. The skipped test is an optional model integration test, so it is not counted as a pass in that run. The validation record also includes several installed-wheel Python/Cognee/dlt combinations, a locked development environment, and lint, format, lock, and wheel checks. Those are recorded results for the listed environments, not proof of every allowed version or backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing with local Cognee stores
&lt;/h2&gt;

&lt;p&gt;Mocked Postman responses are useful for repeatable edge cases, but they cannot show that Cognee’s stores update as expected. A separate local run used a synthetic Postman collection and actual Cognee Data, graph, and vector stores. It covered an initial ingest, unchanged sync, edit, and deletion, with store inspection as well as search.&lt;/p&gt;

&lt;p&gt;The local model-backed setup used cached models; it did not use Cognee Cloud credits or paid model calls. Provider writes were limited to synthetic test collections, and a postflight check confirmed that the original collection remained and the synthetic fixtures were removed.&lt;/p&gt;

&lt;p&gt;The evidence has boundaries: the provider and Cognee runs use a small synthetic workspace and the recorded versions. They do not prove behavior on every backend, every provider permission setup, or every possible partial failure. The package’s &lt;code&gt;README.md&lt;/code&gt; has the quickstart; &lt;code&gt;VALIDATION.md&lt;/code&gt; and &lt;code&gt;VALIDATION_RESULTS.json&lt;/code&gt; describe reproducible commands, environments, and limitations. The authenticated lifecycle requires a Postman API key and a dedicated test workspace.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed in how I build connectors
&lt;/h2&gt;

&lt;p&gt;This started as an API client and document mapper. It became a synchronization system with state, identity, deletion policy, privacy boundaries, and recovery behavior. Those are part of the product contract, so they need tests and documentation of their own.&lt;/p&gt;

&lt;p&gt;I also learned that review is most useful when it produces a sharper test. A comment about a disappearing collection can become a multi-run regression. A redaction example can become a fixture that checks both what must disappear and what ordinary prose must preserve. That turns review from a list of opinions into a growing set of concrete behaviors.&lt;/p&gt;

&lt;p&gt;The work is proposed in &lt;a href="https://github.com/topoteretes/cognee-community/pull/366" rel="noopener noreferrer"&gt;Cognee Community PR #366&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cognee</category>
      <category>postman</category>
      <category>opensource</category>
      <category>learning</category>
    </item>
    <item>
      <title>When a Jira Issue Disappears: Lessons from Building a Cognee Connector</title>
      <dc:creator>Vinayak Sonthalia</dc:creator>
      <pubDate>Sat, 31 Oct 2026 18:33:33 +0000</pubDate>
      <link>https://dev.to/vinayaksonthalia/when-a-jira-issue-disappears-lessons-from-building-a-cognee-connector-4hjb</link>
      <guid>https://dev.to/vinayaksonthalia/when-a-jira-issue-disappears-lessons-from-building-a-cognee-connector-4hjb</guid>
      <description>&lt;h1&gt;
  
  
  When a Jira issue disappears, should its memory disappear too?
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;What we learned building a Jira connector for Cognee: synthetic test data, careful deletion semantics, and the bugs that appeared only after we kept reviewing.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A Jira connector sounds like a data plumbing task: read issues, comments, and attachments, turn them into documents, and make them searchable. The harder question came later: when Jira stops returning something, should Cognee forget it?&lt;/p&gt;

&lt;p&gt;That question turned a small connector project into a lesson in uncertainty, test design, and why “all tests pass” is a milestone—not a guarantee.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with data we could safely break
&lt;/h2&gt;

&lt;p&gt;I created a free Jira Cloud test site and a tiny synthetic project: two issues about Orion retry limits and Vega queue limits, with comments and a text attachment. The content was invented so we could know exactly what the connector should find, edit, and remove without bringing real work data into an early test.&lt;/p&gt;

&lt;p&gt;The first sync produced five documents: two issues, two comments, and one attachment. On an unchanged sync, the documents remained while the connector reused the issue descriptions. Comments and attachment inventories were refreshed independently. A comment can change without a useful parent-issue timestamp change, so relying on the parent cursor alone could leave old words behind.&lt;/p&gt;

&lt;p&gt;We checked the resulting memory in Cognee’s Data records, graph, and six vector collections. Search results alone are not enough evidence that removed content has actually been cleaned up.&lt;/p&gt;

&lt;p&gt;Here is the basic path we wanted to verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Jira issues ─┬─&amp;gt; issue documents ─┐
             ├─&amp;gt; comment documents ├─&amp;gt; complete replacement snapshot ─&amp;gt; Cognee
             └─&amp;gt; attachment text ──┘
                                      │
                                      └─ empty snapshot ─&amp;gt; remove old corpus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  “Missing” is not the same as “deleted”
&lt;/h2&gt;

&lt;p&gt;An issue can disappear from a query because it was deleted. It can also move, stop matching a JQL filter, or become hidden after an access change. Jira search is eventually consistent, too. From a missing result alone, the connector cannot know which explanation is right.&lt;/p&gt;

&lt;p&gt;We made that tradeoff explicit in the API. Callers must choose a policy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;mirror_visibility&lt;/code&gt; replaces the corpus with what the account can currently see. It supports next-sync forgetting when access stays complete and stable.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;require_confirmation&lt;/code&gt; pauses when known documents disappear, so the caller can check the situation and acknowledge specific removals.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neither policy turns absence into proof of deletion. Visibility mirroring can remove content after access changes; confirmation can delay a real deletion. The choice belongs in configuration and documentation, not hidden behind a default.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reviews changed our understanding
&lt;/h2&gt;

&lt;p&gt;The first strict review rounds found problems that happy-path tests had missed. Our main example selected the guarded policy, so it did not demonstrate automatic next-sync removal. An empty snapshot also needed to create a physical staging table; otherwise, after dlt staging state was lost, Cognee could retain old Data records. And our renderer flattened superscript and subscript, changing the meaning of expressions such as &lt;code&gt;10⁻³&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Fixing those issues was not the end. A later review modeled Jira’s general search returning stale membership while reconciliation returned the current result for specific issue IDs. We were adding reconciled results to the general list but ignoring IDs excluded by reconciliation. That kept an issue’s documents even after it stopped matching the selected JQL filter.&lt;/p&gt;

&lt;p&gt;Our first correction removed the issue from the current list. A repeat run exposed a second bug: stale general search could reintroduce it. The final logic reconciles both retained and newly discovered IDs in batches, then lets each complete reconciliation batch replace general search membership for those IDs. This prevents a known filter exit from being resurrected while leaving unknown discovery subject to eventual consistency.&lt;/p&gt;

&lt;p&gt;Another review caught a usability bug: a raw &lt;code&gt;ORDER BY&lt;/code&gt; regular expression rejected a valid filter such as &lt;code&gt;summary ~ "order by"&lt;/code&gt;. We changed the check to distinguish quoted JQL values from a real ordering clause.&lt;/p&gt;

&lt;p&gt;The review-and-fix loop looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Review finding
     ↓
Small reproduction
     ↓
Fix and add regression test
     ↓
Strict review again
     ↓
New edge case? ── yes ──&amp;gt; repeat
     │
     no
     ↓
Rebuild and verify the exact package
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also found two test-harness failures along the way. One subprocess imported an older installed package instead of the source under test. A global HTTP mock counted unrelated telemetry requests as Jira retries. Both taught us to verify what a test actually loaded and intercepted, rather than trust the test’s name or shell exit code.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What failed&lt;/th&gt;
&lt;th&gt;What we changed&lt;/th&gt;
&lt;th&gt;The lesson&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Default workflow did not forget automatically&lt;/td&gt;
&lt;td&gt;Require an explicit removal policy; make the main example choose visibility mirroring&lt;/td&gt;
&lt;td&gt;Make important behavior visible in the API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Empty rebuilt staging left old Cognee records&lt;/td&gt;
&lt;td&gt;Materialize the empty resource table&lt;/td&gt;
&lt;td&gt;Test empty results and recovery, not only populated syncs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Superscript/subscript lost meaning&lt;/td&gt;
&lt;td&gt;Preserve the notation and invalidate old cached rendering&lt;/td&gt;
&lt;td&gt;Text conversion can change meaning, not just formatting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stale general search retained and later resurrected filtered issues&lt;/td&gt;
&lt;td&gt;Reconcile retained and discovered IDs; replace membership per completed batch&lt;/td&gt;
&lt;td&gt;Test repeated runs after the first fix&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quoted “order by” text was rejected&lt;/td&gt;
&lt;td&gt;Detect ordering clauses outside quoted strings&lt;/td&gt;
&lt;td&gt;Validate query structure, not keyword appearances&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subprocess tested an old install; HTTP mock counted telemetry&lt;/td&gt;
&lt;td&gt;Bind subprocesses to the imported package and scope the mock to the Jira client&lt;/td&gt;
&lt;td&gt;Check the test harness itself&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What we verified—and what remains unknown
&lt;/h2&gt;

&lt;p&gt;The final local run passed 69 tests on each of Python 3.11, 3.12, and 3.13. We also tested an authenticated synthetic Jira lifecycle, recovery after downstream ingestion failure, and cleanup after staging was rebuilt. With two overlapping corpora, we removed one corpus’s facts while preserving the other’s, then removed the remaining facts when its owner retired them. We inspected Data, graph, and vector stores through the cleanup steps.&lt;/p&gt;

&lt;p&gt;An independent GPT-6.1 Sol High review found further defects in two later rounds. After the fixes, it reran the source and installed suites and ten additional adversarial probes. The final report found no further actionable issues within its review scope. These are local results—not GitHub CI, a guarantee of correctness, or production experience.&lt;/p&gt;

&lt;p&gt;The connector does not make Jira snapshots atomic. Unknown issues can appear late; access changes can look like deletions; and the first version supports a limited set of attachment formats and unscoped API tokens. The README describes the size, timeout, concurrency, and compatibility limits as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the work stands
&lt;/h2&gt;

&lt;p&gt;The connector is proposed in &lt;a href="https://github.com/topoteretes/cognee-community/pull/462" rel="noopener noreferrer"&gt;PR #462&lt;/a&gt;. As of October 31, 2026, it is open, with no review comments or GitHub checks listed. It has been tested locally against synthetic Jira data and has not been deployed to production.&lt;/p&gt;

&lt;p&gt;The most useful lesson was to test the uncomfortable transitions: a comment changes quietly, an issue leaves a filter, staging disappears, or the snapshot becomes empty. Those are the moments when stale knowledge can survive. Reviewing those cases repeatedly made the connector—and our evidence for it—more trustworthy.&lt;/p&gt;




</description>
      <category>cognee</category>
      <category>jira</category>
      <category>opensource</category>
      <category>learning</category>
    </item>
    <item>
      <title>Building a Grafana Connector for Cognee: What I Learned About Syncing Safely</title>
      <dc:creator>Vinayak Sonthalia</dc:creator>
      <pubDate>Sat, 31 Oct 2026 18:31:52 +0000</pubDate>
      <link>https://dev.to/vinayaksonthalia/building-a-grafana-connector-for-cognee-what-i-learned-about-syncing-safely-2l4h</link>
      <guid>https://dev.to/vinayaksonthalia/building-a-grafana-connector-for-cognee-what-i-learned-about-syncing-safely-2l4h</guid>
      <description>&lt;h1&gt;
  
  
  Building a Grafana Connector for Cognee: What I Learned About Syncing Safely
&lt;/h1&gt;

&lt;p&gt;I’ve been building a Grafana connector for Cognee as part of &lt;a href="https://github.com/topoteretes/cognee/issues/4779" rel="noopener noreferrer"&gt;Issue #4779&lt;/a&gt;. The implementation is in &lt;a href="https://github.com/topoteretes/cognee-community/pull/458" rel="noopener noreferrer"&gt;PR #458&lt;/a&gt;, which is open for maintainer review. This post describes the implementation and lessons so far; it is not a claim that the PR is merged or that every Grafana setup is supported.&lt;/p&gt;

&lt;p&gt;The idea is simple: make selected dashboard context and human annotations searchable alongside other knowledge in Cognee. The engineering turned out to be less about making HTTP requests and more about deciding what the connector can safely conclude from them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the connector reads
&lt;/h2&gt;

&lt;p&gt;The initial connector renders selected dashboards, panel titles and descriptions, and human annotations into documents for Cognee. It does not run dashboard queries or ingest metric values. Identity and selection matter: a dashboard UID is used as the dashboard identity, annotations use their IDs, and each connector resource maps to one separately managed corpus.&lt;/p&gt;

&lt;p&gt;Before implementing it, I studied existing Cognee connector patterns and the Postman connector PR &lt;a href="https://github.com/topoteretes/cognee-community/pull/366" rel="noopener noreferrer"&gt;#366&lt;/a&gt;. That review helped me think about the full lifecycle: extraction, staging, retries, replacement, and cleanup—not only the API call itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The central challenge: when is a snapshot complete?
&lt;/h2&gt;

&lt;p&gt;Consider a sync that sees fewer dashboards than last time. A dashboard might really have been deleted. But access could also have changed, pagination could be incomplete, or a request could have failed. If the connector treats every missing item as deleted, a temporary visibility problem can erase good data.&lt;/p&gt;

&lt;p&gt;The connector therefore validates the read and its authority before allowing a new snapshot to replace the prior staged view. If it cannot establish a usable snapshot, it fails the extraction so that uncertainty does not become deletion.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    A[Start sync] --&amp;gt; B[Check account, role, and required permissions]
    B --&amp;gt; C[Read selected dashboard inventory]
    C --&amp;gt; D[Read annotation history]
    D --&amp;gt; E[Recheck authority and dashboard inventory]
    E --&amp;gt; F{All reads and checks complete?}
    F -- No --&amp;gt; G[Fail extraction; do not publish replacement snapshot]
    F -- Yes --&amp;gt; H[Render selected content and stage snapshot]
    H --&amp;gt; I[Cognee processes documents and cleanup]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;That leads to the principle I’ll carry into future sync work: &lt;strong&gt;absence is meaningful only when the read was complete enough to trust.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Permissions are part of the design
&lt;/h2&gt;

&lt;p&gt;On the tested Grafana release, the organization-wide reads needed for this reconciliation were verified using an organization Admin service account. The connector sends GET requests, but the account role also permits writes. Those are separate facts: read-only request behavior does not mean the credential itself is read-only.&lt;/p&gt;

&lt;p&gt;The PR documents this as a limitation and asks maintainers to decide whether to accept the initial requirement or pursue a narrower authority strategy. The connector refuses a Viewer account because a filtered view could be mistaken for a complete inventory and cause false deletion.&lt;/p&gt;

&lt;p&gt;This was a useful security lesson: explain both the operations your code performs and the privileges held by the credential that performs them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deliberately narrow support
&lt;/h2&gt;

&lt;p&gt;The implementation targets Grafana OSS 12.3.1, organization 1, the default namespace, the &lt;code&gt;v1beta1&lt;/code&gt; dashboard API with classic schema 42, and SQL-backed annotations. Library panels are refused instead of silently omitted. Other Grafana versions, Cloud, organizations, schemas, and annotation stores need separate validation.&lt;/p&gt;

&lt;p&gt;The narrow contract makes the current behavior easier to reason about, but it is not a substitute for broader compatibility work. The PR asks for maintainer input on the initial boundary and calls out validation still needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Incremental work is not an incremental feed
&lt;/h2&gt;

&lt;p&gt;An unchanged dashboard can reuse cached rendered content, reducing repeated rendering and downstream work. However, this connector still reads complete dashboard inventories and scans the configured annotation history. “Incremental” here describes reuse and processing, not a guarantee that Grafana sends only changed records.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart LR
    A[Full provider inventory] --&amp;gt; B{Previously seen revision?}
    B -- Same --&amp;gt; C[Reuse cached rendered row]
    B -- New or changed --&amp;gt; D[Render selected fields]
    C --&amp;gt; E[Build verified replacement snapshot]
    D --&amp;gt; E
    E --&amp;gt; F[Cognee ingestion and reconciliation]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Making this distinction explicit matters for both performance expectations and correctness: the connector must still verify enough of the source to reconcile removals safely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Redaction and selected context
&lt;/h2&gt;

&lt;p&gt;Dashboards can contain more than useful prose. The connector uses an allowlist of selected fields and avoids ingesting query targets, datasource settings, variable values, raw options, and secure configuration. It also applies best-effort filtering for credentials and accepts explicit redaction values for known secrets.&lt;/p&gt;

&lt;p&gt;Redaction is not a guarantee that every secret in arbitrary prose will be detected. That limitation is documented, and users should review selected content and supply known secret strings explicitly. This reinforced another lesson for me: data minimization is stronger than relying on filters after collecting everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I tested it—and what that does not prove
&lt;/h2&gt;

&lt;p&gt;The work includes tests for identity, annotations, permission changes, incomplete reads, selection changes, redaction, retries, and deletion behavior. I also ran installed-wheel tests with Python 3.11, 3.12, and 3.13, plus lifecycle checks against a disposable Grafana instance and local Cognee stores.&lt;/p&gt;

&lt;p&gt;Those results provide evidence for the exercised paths; they do not prove universal correctness. Broader Grafana versions, other storage backends, arbitrary cross-store failure recovery, and every permission or concurrency interleaving remain outside what these checks establish. The PR is still open for review, and maintainer feedback may change the design or scope.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I’m taking forward
&lt;/h2&gt;

&lt;p&gt;This project changed how I think about connectors. A connector is not just an API wrapper. It is a trust boundary and a data lifecycle: it decides what to collect, how to identify it, when a snapshot is complete, how to handle uncertainty, and when removal is safe.&lt;/p&gt;

&lt;p&gt;The hardest part was not fetching dashboards. It was making sure the system does not confuse “I can’t see it right now” with “it no longer exists.” That distinction—and the need to document limits honestly—is the learning I’ll take into the next integration.&lt;/p&gt;

&lt;p&gt;The next step is maintainer review of the permission model, support boundary, and remaining validation work in &lt;a href="https://github.com/topoteretes/cognee-community/pull/458" rel="noopener noreferrer"&gt;PR #458&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cognee</category>
      <category>grafana</category>
      <category>opensource</category>
      <category>learning</category>
    </item>
    <item>
      <title>I Built an AI That Forgets on Purpose — and It Made It Smarter</title>
      <dc:creator>Vinayak Sonthalia</dc:creator>
      <pubDate>Sun, 05 Jul 2026 16:02:52 +0000</pubDate>
      <link>https://dev.to/vinayaksonthalia/i-built-an-ai-that-forgets-on-purpose-and-it-made-it-smarter-5ci</link>
      <guid>https://dev.to/vinayaksonthalia/i-built-an-ai-that-forgets-on-purpose-and-it-made-it-smarter-5ci</guid>
      <description>&lt;p&gt;&lt;em&gt;It's 3am, your site is down, and your AI confidently tells you to fix a server that was deleted a month ago. Here's why I spent a week building the delete key.&lt;/em&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%2F9rzjd02xkf56k4gfipnh.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%2F9rzjd02xkf56k4gfipnh.png" alt="The AI That Forgets on Purpose" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The night my own assistant lied to me
&lt;/h2&gt;

&lt;p&gt;I was building an incident-triage assistant — the kind of thing an on-call engineer asks for help at 3am when the site is down. Feed it your team's runbooks, ask it what to check, get a calm answer back. It worked. I was pretty happy with it.&lt;/p&gt;

&lt;p&gt;Then one evening, while testing, it told me to go fix a server we'd switched off a month earlier.&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%2Fcxmdgfuwuu3uei4mikzy.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%2Fcxmdgfuwuu3uei4mikzy.png" alt="3am panic" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Me:&lt;/strong&gt; auth-service is slow, what do I check?&lt;br&gt;
&lt;strong&gt;Assistant:&lt;/strong&gt; Check the legacy-cache — flush and resize the cluster!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Confident. Detailed. Completely wrong. The legacy-cache didn't exist anymore. And here's the thing that stuck with me: &lt;strong&gt;the assistant hadn't malfunctioned.&lt;/strong&gt; It did exactly what I built it to do — remember. It just remembered something it should have forgotten.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug wasn't a bug. It was the whole design.
&lt;/h2&gt;

&lt;p&gt;Look at how everyone builds "AI memory" right now — they're all racing in one direction: &lt;strong&gt;remember more.&lt;/strong&gt; More documents, longer history, bigger context.&lt;/p&gt;

&lt;p&gt;It's genuinely useful — until you notice the quiet assumption holding it all together: &lt;em&gt;every fact we remember stays true forever.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Facts do not do that.&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%2F527u83mdkwupj9kpfmjl.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%2F527u83mdkwupj9kpfmjl.png" alt="The hoarder problem" width="799" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Servers get retired. People change teams. Last year's clever fix becomes this year's outage. A bigger memory just hands you &lt;strong&gt;more stale facts, with no way to tell "still true" from "used to be true."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Think of your phone's contacts: if you only ever &lt;em&gt;add&lt;/em&gt; numbers, eventually you call an old friend and a stranger picks up. The bug was never a number you forgot — it was a number you &lt;strong&gt;kept when you shouldn't have.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So I stopped asking &lt;em&gt;"how much can it remember?"&lt;/em&gt; and started asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Can it stop remembering the things that have stopped being true?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I named the project &lt;strong&gt;Lethe&lt;/strong&gt;, after the river of forgetting. (I'm not subtle.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Watch it forget, live
&lt;/h2&gt;

&lt;p&gt;I retire the &lt;code&gt;legacy-cache&lt;/code&gt; — decommission it, gone — and ask the &lt;strong&gt;exact same question&lt;/strong&gt; as before, word for word:&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%2Fddsf91frjlt4l5o8kauc.gif" 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%2Fddsf91frjlt4l5o8kauc.gif" alt="The hero flip — same question, one delete, different answer" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt; "Check the legacy-cache, flush and resize the cluster." &lt;em&gt;(go fight the ghost)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After the delete, same question:&lt;/strong&gt; "Check the session-store connection pool and its hit rate…"&lt;/p&gt;

&lt;p&gt;The answer &lt;strong&gt;flips.&lt;/strong&gt; And when I pushed it — &lt;em&gt;"okay, then what IS the legacy-cache?"&lt;/em&gt; — it said:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The legacy-cache is not documented in the runbooks."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not &lt;em&gt;"I remember more."&lt;/em&gt; Instead: &lt;strong&gt;"I stopped remembering the wrong thing"&lt;/strong&gt; — and it &lt;em&gt;admits it doesn't know&lt;/em&gt; rather than confidently inventing something. Which, for an AI, is basically emotional maturity.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works (the two-minute version)
&lt;/h2&gt;

&lt;p&gt;Lethe is built on &lt;a href="https://www.cognee.ai/" rel="noopener noreferrer"&gt;Cognee&lt;/a&gt;, an open-source memory engine. You feed it runbooks as &lt;strong&gt;plain English&lt;/strong&gt; — no tags, no schema — and one &lt;code&gt;cognify()&lt;/code&gt; call builds &lt;strong&gt;two stores at once&lt;/strong&gt;: a knowledge graph (Kùzu) and a vector index (LanceDB).&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%2Fzaexvmfal70epx2ptd4a.gif" 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%2Fzaexvmfal70epx2ptd4a.gif" alt="The live knowledge graph" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ask a question → vector search finds what's &lt;em&gt;relevant&lt;/em&gt;, the graph adds what's &lt;em&gt;connected&lt;/em&gt;, and the model writes one runbook-style answer — every claim cited to its source.&lt;/p&gt;

&lt;p&gt;And when a system dies? &lt;code&gt;forget()&lt;/code&gt; — a &lt;strong&gt;real hard delete&lt;/strong&gt;: raw files, graph nodes, edges, embeddings. Gone. With a printed receipt and a live re-query proving it.&lt;/p&gt;

&lt;p&gt;There's a gentler layer too: a &lt;strong&gt;curation loop&lt;/strong&gt; that scores memory health, reversibly down-weights aging runbooks, and queues true deletions for a &lt;em&gt;human&lt;/em&gt; to approve. Nothing gets hard-deleted without a person saying yes.&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%2F76u8tf3147ba2g1ks07b.gif" 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%2F76u8tf3147ba2g1ks07b.gif" alt="The curation loop" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Then I tried, on purpose, to prove myself wrong
&lt;/h2&gt;

&lt;p&gt;Cognee advertises several superpowers beyond forgetting. Instead of listing them all as &lt;em&gt;my&lt;/em&gt; features, I raced them against plain, boring RAG — same docs, same model, temperature zero:&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%2Fny1lkp7ihofmw21m0skc.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%2Fny1lkp7ihofmw21m0skc.png" alt="The honest bake-off" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Multi-hop? &lt;strong&gt;Tied&lt;/strong&gt; at this scale. Learning from feedback? &lt;strong&gt;No clean win.&lt;/strong&gt; Blast-radius? &lt;strong&gt;Tied.&lt;/strong&gt; Only &lt;strong&gt;forget&lt;/strong&gt; produced something RAG-with-the-same-stack fundamentally cannot do. So I cut the three ties as headline claims and built the whole product around the one provable win.&lt;/p&gt;

&lt;p&gt;Then I measured forgetting itself — &lt;strong&gt;twice&lt;/strong&gt;, with a blind judge from a different model family scoring every answer:&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%2Flen6ygsy5dymorq8cly1.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%2Flen6ygsy5dymorq8cly1.png" alt="Forgetting, measured" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(Fun confession: my &lt;em&gt;first&lt;/em&gt; benchmark got thrown out — it graded whether the forgotten word was absent, which is circular. The blind-judge version grades &lt;em&gt;correctness&lt;/em&gt;.)&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug that almost killed the demo
&lt;/h2&gt;

&lt;p&gt;For a while, the demo was haunted: the same question usually returned a full sentence — but &lt;em&gt;sometimes&lt;/em&gt; it fired back a single word: &lt;strong&gt;"legacy-cache."&lt;/strong&gt; Just the word.&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%2Fhxk8goupzbr735it9vpy.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%2Fhxk8goupzbr735it9vpy.png" alt="The case of the one-word answer" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I cleared the suspects one by one — the build (glitch happened on the &lt;em&gt;same&lt;/em&gt; saved graph), my phrasing, retrieval (the assembled context was rich and correct &lt;em&gt;every time&lt;/em&gt;). The culprit? A line buried in the tool's &lt;strong&gt;default prompt&lt;/strong&gt;: &lt;em&gt;"answer as briefly as possible."&lt;/em&gt; On a vague question, that collapses to the single most relevant word. The model wasn't broken — it was obeying its instructions &lt;em&gt;a little too well.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One custom prompt later, three separate bugs disappeared at once. &lt;strong&gt;When an LLM writes your final answer, the instruction you hand it is the most powerful lever you have.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Go poke it yourself
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🌊 &lt;strong&gt;Try it live (no signup):&lt;/strong&gt; &lt;a href="https://vinayaksonthalia-lethe.hf.space" rel="noopener noreferrer"&gt;vinayaksonthalia-lethe.hf.space&lt;/a&gt; — ask, decommission, re-ask, watch it flip&lt;/li&gt;
&lt;li&gt;🎬 &lt;strong&gt;2-minute demo:&lt;/strong&gt; &lt;a href="https://youtu.be/3840gxTZWxY" rel="noopener noreferrer"&gt;youtu.be/3840gxTZWxY&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📚 &lt;strong&gt;Every design decision, documented:&lt;/strong&gt; &lt;a href="https://vinayaksonthalia-lethe.hf.space/learn" rel="noopener noreferrer"&gt;30 chapters at /learn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💻 &lt;strong&gt;Code:&lt;/strong&gt; &lt;a href="https://github.com/vinayaksonthalia/lethe" rel="noopener noreferrer"&gt;github.com/vinayaksonthalia/lethe&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everyone's building memory that only grows. Lethe is the other half — &lt;strong&gt;memory that knows when to let go.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks for reading. Now go delete something.&lt;/em&gt; 🌊&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built solo in one week by Vinayak Sonthalia (final-year B.Tech) for the WeMakeDevs × Cognee hackathon.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>devops</category>
      <category>hackathon</category>
    </item>
  </channel>
</rss>
