Skip to content

memory: port sync dedup fix, tolerate missing transcripts, document rem workflow - #154

Merged
yourconscience merged 1 commit into
mainfrom
memory/reconcile-upstream
Aug 25, 2026
Merged

memory: port sync dedup fix, tolerate missing transcripts, document rem workflow#154
yourconscience merged 1 commit into
mainfrom
memory/reconcile-upstream

Conversation

@yourconscience

@yourconscience yourconscience commented Aug 25, 2026

Copy link
Copy Markdown
Owner

Summary by Sourcery

Make memory synchronization deduplicate exported facts reliably, tolerate unavailable transcripts, and document the current report-first workflow.

Bug Fixes:

  • Prevent duplicate memory facts from being re-exported when existing vault entries contain overlapping or serialized records.
  • Allow memory hooks to tolerate missing transcript files without failing.

Enhancements:

  • Document the memory layer as the canonical upstream and describe the report-first rem workflow, memory tiers, layout, and relationship to deployed user repositories.

Documentation:

  • Replace the memory README’s detailed legacy guidance with current rem workflow and setup documentation.

@sourcery-ai

sourcery-ai Bot commented Aug 25, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR ports the sync deduplication fix by matching exact serialized Hermes entries and comparing SHA-256 fingerprints of normalized content, makes missing transcript files non-fatal, and updates the memory README to document the canonical upstream model and report-first rem workflow.

Sequence diagram for tolerant transcript reading

sequenceDiagram
    participant Hook
    participant Reader
    participant Transcript
    Hook->>Reader: read_transcript(path)
    Reader->>Transcript: Check path.exists()
    alt transcript missing
        Reader-->>Hook: [], None, None
    else transcript present
        Reader->>Transcript: Read transcript
        Reader-->>Hook: Messages and metadata
    end
Loading

Flow diagram for exact memory sync deduplication

flowchart TD
    A[Hermes memory entries] --> B[Read vault knowledge]
    B --> C[Find exact serialized entries]
    C --> D[Normalize content]
    D --> E[SHA-256 fingerprints]
    E --> F{Fingerprint already exported?}
    F -->|No| G[Append entry to vault]
    F -->|Yes| H[Skip duplicate]
Loading

File-Level Changes

Change Details Files
Reposition the memory documentation around the canonical upstream and the report-first rem workflow.
  • Document rem add, dream, search, and sync operations.
  • Clarify memory tiers, repository relationships, layout, and deployment flow.
  • Remove the older detailed dream-review and canonical-path guidance from the top-level README.
memory/README.md
Make transcript ingestion tolerant of paths that do not yet exist.
  • Return an empty transcript result for a missing path.
  • Continue to reject existing paths that are not regular files.
memory/lib/basic_memory.py
Fix memory-to-vault deduplication to recognize only exact exported entries and compare normalized fingerprints.
  • Extract exact serialized entries from sync sections using longest-first overlap handling.
  • Hash normalized entries for stable duplicate detection.
  • Track newly exported fingerprints during the same sync to prevent duplicate additions.
memory/lib/sync.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@yourconscience
yourconscience merged commit 52264a9 into main Aug 25, 2026
5 checks passed
@yourconscience
yourconscience deleted the memory/reconcile-upstream branch August 25, 2026 10:14

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="memory/lib/sync.py" line_range="71-73" />
<code_context>
+    occupied: list[tuple[int, int]] = []
+    fingerprints: set[bytes] = set()
+    for entry in sorted(memory_entries, key=len, reverse=True):
+        pattern = re.compile(
+            rf"(?m)^- {re.escape(entry)}\n(?=- |\n## Sync |\Z)"
+        )
+        for match in pattern.finditer(text):
+            start, end = match.span()
+            if any(start < used_end and used_start < end for used_start, used_end in occupied):
</code_context>
<issue_to_address>
**issue (bug_risk):** When `sessions/knowledge.md` contains valid Markdown list items separated by a blank line, the regex does not recognize the exported entry because it requires the next line to start immediately with `- ` or `## Sync`. `memory_to_vault` therefore appends the same fact again on every sync.

**Triggers:** When an existing knowledge export has blank lines between bullet entries.

**Suggested fix:** Allow whitespace between serialized entries, or parse the sync sections as Markdown bullets instead of requiring adjacent `- ` lines.

```suggestion
        pattern = re.compile(
            rf"(?m)^- {re.escape(entry)}\n(?=\s*(?:- |## Sync |\Z))"
        )
```
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 1 finding to address first, and the stricter serialized-entry matching can fail to recognize facts already present in the vault, causing duplicate records to be written. Reverting prevents further duplicates, but records already added remain and require cleanup or rerunning consolidation.

Blocking findings: memory/lib/sync.py:73


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread memory/lib/sync.py
Comment on lines +71 to +73
pattern = re.compile(
rf"(?m)^- {re.escape(entry)}\n(?=- |\n## Sync |\Z)"
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): When sessions/knowledge.md contains valid Markdown list items separated by a blank line, the regex does not recognize the exported entry because it requires the next line to start immediately with - or ## Sync. memory_to_vault therefore appends the same fact again on every sync.

Triggers: When an existing knowledge export has blank lines between bullet entries.

Suggested fix: Allow whitespace between serialized entries, or parse the sync sections as Markdown bullets instead of requiring adjacent - lines.

Suggested change
pattern = re.compile(
rf"(?m)^- {re.escape(entry)}\n(?=- |\n## Sync |\Z)"
)
pattern = re.compile(
rf"(?m)^- {re.escape(entry)}\n(?=\s*(?:- |## Sync |\Z))"
)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c998e83c37

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread memory/README.md
rem dream # consolidation report (report-only)
rem dream --apply # collapse exact-duplicate records
rem search "quota preferences" # semantic search via memsearch
rem sync # flush the vault via knowledge-sync

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore the private-remote warning before advertising sync

When a user configures the knowledge vault with a public Git remote and follows this new rem sync workflow, knowledge-sync stages every vault change with git add -A and pushes it, even though the vault contains private session and profile data. This rewrite removes the repository's only warning that the vault must never be pushed publicly, so retain a prominent private-remote warning alongside this command.

Useful? React with 👍 / 👎.

Comment thread memory/lib/sync.py
fingerprints: set[bytes] = set()
for entry in sorted(memory_entries, key=len, reverse=True):
pattern = re.compile(
rf"(?m)^- {re.escape(entry)}\n(?=- |\n## Sync |\Z)"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Match exported entries using their normalized form

When Hermes changes only an entry's case or punctuation after an earlier export (for example, prefers pnpm. becoming Prefers pnpm), this exact-text regex cannot find the existing bullet even though normalize() treats both forms as identical. Each subsequent sync therefore appends another duplicate section; preserve normalized comparison while parsing complete serialized entries, and cover normalized variants in a focused sync test.

AGENTS.md reference: AGENTS.md:L42-L46

Useful? React with 👍 / 👎.

Comment thread memory/README.md
Comment on lines +9 to +10
`memory/lib`, `memory/hooks`, and `memory/tools`; `dotagents sync` builds the
tools and this repo carries the tested reference implementations.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stop claiming setup deploys the memory tools

For users installing the release CLI, dotagents setup walks StarterAssets, whose embed list contains only memory/hooks and memory/lib, not memory/tools; consequently the created ~/.agents repository has no tool sources and dotagents sync has nothing to build. The newly documented rem workflow therefore fails with command-not-found after the documented setup, so either include the tools in the starter assets or document the separate installation step.

AGENTS.md reference: AGENTS.md:L48-L50

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant