<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Blvck Shawn]]></title><description><![CDATA[Blvck Shawn]]></description><link>https://cognee-evernote.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Sun, 01 Nov 2026 21:07:01 GMT</lastBuildDate><atom:link href="https://cognee-evernote.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Evernote Meets Cognee - Without Stale Memories]]></title><description><![CDATA[How I built a Cognee Community connector that ingests Evernote notes, syncs only what changed, and removes what you delete, tested offline and honest about…]]></description><link>https://cognee-evernote.hashnode.dev/evernote-meets-cognee-without-stale-memories-mvulzwvx</link><guid isPermaLink="true">https://cognee-evernote.hashnode.dev/evernote-meets-cognee-without-stale-memories-mvulzwvx</guid><category><![CDATA[Cognee]]></category><category><![CDATA[mergetober]]></category><category><![CDATA[WeMakeDevs]]></category><dc:creator><![CDATA[Laxman Joshi]]></dc:creator><pubDate>Fri, 30 Oct 2026 06:46:35 GMT</pubDate><content:encoded><![CDATA[<h1>Teaching AI to Forget: Building an Evernote Connector for Cognee</h1>
<h2>Introduction</h2>
<p>AI systems are becoming more useful when they can work with information beyond a single conversation. Project notes, meeting records, technical documentation, and personal knowledge bases can all provide context that a model would otherwise lack.</p>
<p>But connecting an AI system to a source of information is only part of the problem. That information changes over time. Notes get edited, moved to different notebooks, archived, or deleted. If an AI system continues to retrieve outdated information, its answers can become misleading even when the original data was once correct.</p>
<p>This is the problem addressed by an Evernote connector developed for the Cognee Community project. The implementation focuses on bringing Evernote notes into Cognee, synchronizing changes incrementally, and representing deleted or out-of-scope notes so that they can be removed from the ingested dataset.</p>
<p>The interesting part is not simply importing notes. It is handling the changes that happen after the initial import.</p>
<h2>Why importing data once is not enough</h2>
<p>Consider a simple example.</p>
<p>A team stores its service-restart procedure in Evernote. An AI system ingests the note and makes its contents available through its knowledge pipeline. A week later, the procedure changes. Eventually, the old note is deleted.</p>
<p>If the integration only supports the initial import, the AI system may continue to retrieve information from the previous version. The source has changed, but the ingested representation has not necessarily followed it.</p>
<p>There are two related problems here:</p>
<ul>
<li><em>Synchronization:</em> Detecting new and updated information without repeatedly importing everything.</li>
<li><em>Deletion handling:</em> Ensuring that information removed from the source does not remain indefinitely in the destination dataset.</li>
</ul>
<p>The second problem deserves particular attention. Updating a record is relatively straightforward when the new version is available. A deleted record is different: the connector must recognize that the absence of the original note is meaningful and communicate that change to the destination.</p>
<p>A reliable integration needs to account for both.</p>
<h2>Understanding the Evernote–Cognee integration</h2>
<p>Evernote stores notes along with organizational information such as notebooks and tags. Cognee provides a pipeline for turning source information into data that can be used by its AI memory and search workflows.</p>
<p>The connector described in the project adds a bridge between these systems.</p>
<p>Its main responsibilities include:</p>
<ul>
<li>Retrieving Evernote notes and their notebook and tag metadata.</li>
<li>Converting Evernote's ENML note content into plain text.</li>
<li>Supporting notebook and tag filters.</li>
<li>Tracking changes between synchronization runs.</li>
<li>Representing deleted or out-of-scope notes as deletion markers.</li>
<li>Passing the resulting records through a data-loading and ingestion pipeline.</li>
</ul>
<p>The implementation lives in the Cognee Community connector package rather than being described as a built-in Evernote integration in Cognee's core.</p>
<p>The associated work is documented in <a href="https://github.com/topoteretes/cognee-community/pull/559">Cognee Community pull request #559</a>, which addresses <a href="https://github.com/topoteretes/cognee/issues/4728">Cognee issue #4728</a>.</p>
<h2>How the connector works</h2>
<p>The integration can be understood as a sequence of steps:</p>
<p><em>Evernote → Synchronization API → Connector → Data-loading pipeline → Cognee dataset</em></p>
<p>After ingestion, Cognee's graph and search workflows can be used to work with the resulting information.</p>
<h3>1. Retrieving and preparing notes</h3>
<p>The connector retrieves notes from Evernote and preserves notebook and tag names as metadata. Evernote's note content is stored in ENML, an XML-based format, so the connector converts it into plain text before ingestion.</p>
<p>This conversion is important because source formats are not always directly suitable for downstream processing. An integration needs to extract the useful content without carrying unnecessary source-format structure into the text representation.</p>
<p>The connector also supports selecting notebooks and tags. This allows users to limit the information they ingest instead of automatically importing every note in an account.</p>
<h3>2. Synchronizing changes incrementally</h3>
<p>Fetching every note during every run would be wasteful for a knowledge base that changes regularly.</p>
<p>The implementation uses Evernote synchronization chunks and an update-sequence-number (USN) cursor to track progress. The cursor records the position from which a subsequent synchronization should continue.</p>
<p>Conceptually, the process works like this:</p>
<ol>
<li>Start from the last successfully saved synchronization position.</li>
<li>Request the changes available after that position.</li>
<li>Process the relevant records.</li>
<li>Save the updated position only after the run completes successfully.</li>
</ol>
<p>This design helps avoid unnecessarily retrieving the entire account for every routine synchronization.</p>
<p>It also addresses an important failure scenario. If a run fails before completion, the connector should not advance its saved cursor as though all changes had been processed. Retrying from the previous position gives the failed work another opportunity to complete.</p>
<p>Incremental synchronization is therefore not just a performance optimization. Its correctness depends on how progress is recorded and how failures are handled.</p>
<h3>3. Handling deleted notes</h3>
<p>Deletion handling is one of the most significant parts of this implementation.</p>
<p>A connector cannot rely exclusively on receiving the current contents of a note. It also needs a way to represent the fact that a note should no longer be present in the destination.</p>
<p>The implementation uses deletion markers, commonly called tombstones, through the data-loading pipeline. It identifies notes that have been trashed, permanently deleted, or moved outside the selected ingestion scope.</p>
<p>These records allow the destination dataset to process removals rather than treating every synchronization as a process that only adds or updates information.</p>
<p>The distinction between deletion and filtering matters. If a user initially selects one notebook and later changes the selected scope, records that are no longer eligible for ingestion may also need to be reconciled. Otherwise, the destination can retain content that the current configuration no longer includes.</p>
<p>The connector's handling of these cases is intended to keep the ingested dataset aligned with the source and the selected scope.</p>
<p>However, a deletion marker being processed by a data-loading pipeline does not automatically prove that every downstream representation has been cleaned up. That requires separate verification.</p>
<h2>The difficult edge cases</h2>
<p>The general architecture is easy to describe. Making synchronization dependable requires handling less obvious situations.</p>
<h3>What happens when a synchronization fails?</h3>
<p>Saving the cursor only after a successful run helps prevent the connector from skipping changes after an incomplete run.</p>
<p>This is an important safeguard, although it does not eliminate every possible failure mode. A complete production assessment would also examine repeated retries, partially completed destination writes, duplicate processing, and recovery after interruptions.</p>
<p>The goal is not to assume failures will never occur. It is to make sure that failures do not silently leave the source and destination inconsistent.</p>
<h3>What happens when a full resynchronization is necessary?</h3>
<p>A stored cursor cannot always be trusted indefinitely. The implementation checks Evernote's synchronization metadata and can initiate a full resynchronization when the server's state indicates that the previous cursor is no longer suitable.</p>
<p>A full resynchronization introduces another challenge: notes deleted while the cursor was stale might not appear in the current listing.</p>
<p>To address this, the connector tracks note IDs previously ingested and reconciles them against the completed listing. The reconciliation is performed only after the full listing succeeds.</p>
<p>That last detail is important. A partial listing caused by a failure must not be mistaken for a complete account snapshot; otherwise, valid records could be incorrectly removed.</p>
<h3>What happens when notes and notebook metadata arrive in different orders?</h3>
<p>The article also identifies an ordering issue in Evernote's synchronization chunks. A note may arrive before the connector has received the corresponding notebook information.</p>
<p>The implementation addresses this by collecting names first and retrieving note bodies in a second pass.</p>
<p>This is a useful example of why API integrations need to account for the structure and ordering of the data returned by the source, rather than assuming related objects will always arrive together.</p>
<h2>Authentication and compatibility</h2>
<p>The connector includes an OAuth 1.0a helper and support for an existing access token. The implementation also uses a Python SDK dependency identified in the pull request as evernote-plus==1.28.1.dev2, a prerelease version.</p>
<p>These details matter when evaluating the connector beyond its basic data flow.</p>
<p>Authentication needs to be checked against the actual service, dependencies need to remain compatible with the runtime environment, and API behavior needs to be validated against real responses. A successful test with a simulated client cannot establish that all these conditions hold in a live deployment.</p>
<p>The pull request specifically states that live Evernote access was unavailable during development. Its OAuth endpoints were checked against another open-source implementation but were not exercised against the live service.</p>
<p>That limitation should be considered when assessing the current level of verification.</p>
<h2>What the tests establish—and what they do not</h2>
<p>The implementation reports 95 offline tests across Python 3.11, 3.12, and 3.13.</p>
<p>According to the pull request, these tests cover areas including the client wrapper, ENML conversion, synchronization chunks, cursor handling, retries, resynchronization, OAuth-related logic, and ingestion scenarios involving updates and deletions. The reported test scenarios also include a 60-note batch.</p>
<p>This is useful evidence that the connector's behavior has been exercised under controlled conditions.</p>
<p>However, the testing boundary matters.</p>
<p>The tests use fake Evernote clients rather than a live Evernote account. The author also explicitly notes that deletion tests verify the removal of Cognee Data records but do not assert cleanup of graph and vector representations.</p>
<p>These are meaningful limitations, not reasons to dismiss the work.</p>
<p>They identify the next areas that need verification:</p>
<ul>
<li>Authentication and API calls against a live Evernote account.</li>
<li>Correct handling of actual synchronization responses and server-side changes.</li>
<li>End-to-end deletion behavior across the relevant downstream stores.</li>
<li>Recovery from interrupted runs and repeated synchronization.</li>
<li>Compatibility with the required SDK and the supported Cognee versions.</li>
</ul>
<p>Until these areas are verified, it would be premature to describe the connector as fully validated in production or to claim that all forms of stale AI memory have been eliminated.</p>
<h2>Why this work matters</h2>
<p>The broader engineering lesson is that an AI memory integration must do more than make information available.</p>
<p>It must also manage changes to that information over time.</p>
<p>Incremental synchronization helps reduce unnecessary work. Cursor management provides a way to track progress. Deletion markers communicate removals to the destination. Reconciliation helps recover when the incremental history is no longer sufficient.</p>
<p>Together, these mechanisms address a practical problem that can otherwise be overlooked when building AI systems around external knowledge sources.</p>
<p>There is also an important distinction between removing a source record and proving that every derived representation is gone. If information has been processed into other stores, those stores need their own cleanup guarantees and tests.</p>
<p>For AI systems that rely on retrieved context, this is more than a data-engineering detail. The quality of an answer depends partly on whether the information available to the system still reflects the intended source of truth.</p>
<h2>Conclusion</h2>
<p>The Evernote connector for Cognee Community is a useful example of the engineering work involved in maintaining an AI-connected knowledge base.</p>
<p>Its design addresses several important concerns: incremental synchronization, persistent cursor management, deletion markers, filtering, and recovery through full resynchronization. The reported offline tests provide evidence for these behaviors under simulated conditions.</p>
<p>At the same time, the implementation's documented limitations should remain part of the assessment. Live Evernote behavior has not been verified in the reported work, and downstream graph and vector cleanup is not established by the deletion tests described in the pull request.</p>
<p>The next step is therefore not simply to add more features. It is to validate the integration against the real service and confirm that updates and deletions propagate correctly through the entire ingestion and retrieval pipeline.</p>
<p>A knowledge system should not only learn from new information. It should also have a reliable way to stop relying on information that no longer belongs there.</p>
<h3>References</h3>
<ul>
<li><a href="https://cognee-evernote.hashnode.dev/evernote-meets-cognee-without-stale-memories">Original article: Evernote Meets Cognee — Without Stale Memories</a></li>
<li><a href="https://github.com/topoteretes/cognee-community/pull/559">Cognee Community pull request #559: Evernote data-source connector</a></li>
<li><a href="https://github.com/topoteretes/cognee/issues/4728">Cognee issue #4728: Add Evernote data-source connector</a></li>
<li><a href="https://dev.evernote.com/documentation/reference/NoteStore.html">Evernote NoteStore API documentation</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Evernote Meets Cognee - Without Stale Memories]]></title><description><![CDATA[How I built an Evernote connector for Cognee that ingests notes, syncs only changes with a USN cursor, and removes deleted notes from AI memory.]]></description><link>https://cognee-evernote.hashnode.dev/evernote-meets-cognee-without-stale-memories</link><guid isPermaLink="true">https://cognee-evernote.hashnode.dev/evernote-meets-cognee-without-stale-memories</guid><category><![CDATA[Cognee]]></category><category><![CDATA[mergetober]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[WeMakeDevs]]></category><category><![CDATA[Evernote]]></category><category><![CDATA[Pyhton]]></category><category><![CDATA[AI]]></category><category><![CDATA[llm]]></category><dc:creator><![CDATA[Laxman Joshi]]></dc:creator><pubDate>Fri, 30 Oct 2026 04:03:50 GMT</pubDate><content:encoded><![CDATA[<p><em>Illustrative scenario below. I did not have access to a live Evernote account while building this, so everything described as "tested" was tested offline against fake Evernote clients.</em></p>
<h2>The problem</h2>
<p>Years of runbooks, meeting notes, and project write-ups often sit in Evernote, where an AI assistant can't use them. Exporting them once doesn't help: notes change, and notes are trashed or deleted. An assistant that keeps answering from a note you deleted last week is worse than one that has never seen it.</p>
<h2>What I built</h2>
<p>A connector for Cognee Community, <code>packages/connector/evernote/</code>, that turns an Evernote account into Cognee memory:</p>
<ul>
<li><strong>Ingestion:</strong> notes with their notebook and tag names; ENML converted to plain text.</li>
<li><strong>Incremental sync:</strong> Evernote's sync chunks and a persisted update-sequence-number (USN) cursor, so later runs only fetch what changed.</li>
<li><strong>Deletions:</strong> trashed, expunged, or out-of-scope notes are removed from Cognee's dataset.</li>
<li><strong>Scope:</strong> choose notebooks and/or tags to ingest.</li>
<li><strong>Auth:</strong> 3-legged OAuth 1.0a helper, or an existing token.</li>
</ul>
<h2>How it fits together</h2>
<p>Evernote -&gt; sync chunks -&gt; connector (two-pass) -&gt; dlt resource (merge + tombstones) -&gt; Cognee ingestion -&gt; dataset (and cognify -&gt; graph/search)</p>
<p>The connector follows the pattern of the existing OneNote and Google Drive connectors: a dlt resource with <code>merge</code> and a <code>_deleted</code> hard-delete column, so Cognee's existing cleanup removes records that disappear.</p>
<h2>Using it</h2>
<pre><code class="language-python">import cognee
from cognee_community_connector_evernote import evernote_source

source = evernote_source(notebook_names=["Engineering"], tag_names=["runbook"])
await cognee.remember(source, dataset_name="evernote",
                      write_disposition="merge", max_rows_per_table=0)
answer = await cognee.search(
    query_text="How do we restart the payments service?",
    query_type=cognee.SearchType.GRAPH_COMPLETION, datasets=["evernote"])
</code></pre>
<p>No Evernote account? <code>python examples/example.py --fake</code> runs the whole flow, including a trashed note being removed, against an in-memory client.</p>
<h2>How the incremental sync works</h2>
<p>The cursor lives in dlt's resource state and is written only after a run has completed, so a failed run is retried from the old cursor and never skips changes. If Evernote's <code>fullSyncBefore</code> is newer than the last sync, or the server's update count is lower than the cursor, the connector does a full resync. A full resync cannot see notes deleted while the cursor was stale, so the connector tracks the note IDs it ingested and reconciles them, but only after the complete listing has succeeded.</p>
<h2>Lessons learned</h2>
<ul>
<li><strong>The Python SDK is only Thrift stubs.</strong> I had to write my own client wrapper: lazy note-store URL, retries, rate-limit handling, and error mapping that never leaks the token.</li>
<li><strong>Evernote's filter has a trap.</strong> The sync-chunk filter rejects <code>notebookGuids</code> combined with <code>includeExpunged</code>, so scoping has to happen in the connector or deletions are lost.</li>
<li><strong>Notes can arrive before their notebook.</strong> Chunks are ordered by USN, so the connector collects names first and fetches note bodies second.</li>
<li><strong>Empty notes create ghost records</strong>, so they become tombstones instead.</li>
</ul>
<h2>What was tested, and what wasn't</h2>
<ul>
<li>95 offline tests (Python 3.11, 3.12, 3.13), including real Cognee ingestion with updates, incremental runs, deletions, and a 60-note batch.</li>
<li>Deletion tests check that the Data records are removed; graph and vector cleanup is not asserted in this package.</li>
<li><strong>Not tested against a live Evernote account.</strong> Evernote's developer program appears to be gated. The OAuth endpoints were confirmed against another open-source implementation, not exercised live.</li>
<li>The SDK is the pre-release <code>evernote-plus==1.28.1.dev2</code>.</li>
</ul>
<h2>Links</h2>
<ul>
<li>Issue: <a href="https://github.com/topoteretes/cognee/issues/4728">https://github.com/topoteretes/cognee/issues/4728</a></li>
<li>Pull request: <a href="https://github.com/topoteretes/cognee-community/pull/559">https://github.com/topoteretes/cognee-community/pull/559</a></li>
<li>Package: <a href="https://github.com/blvckShawn/cognee-community/tree/feature/evernote-connector-4728/packages/connector/evernote">https://github.com/blvckShawn/cognee-community/tree/feature/evernote-connector-4728/packages/connector/evernote</a></li>
</ul>
]]></content:encoded></item></channel></rss>