Skip to content

fix(memory-sync): reliable per-source sync indicators and counters#3308

Merged
graycyrus merged 5 commits into
tinyhumansai:mainfrom
graycyrus:fix/memory-sync-indicators
Jun 5, 2026
Merged

fix(memory-sync): reliable per-source sync indicators and counters#3308
graycyrus merged 5 commits into
tinyhumansai:mainfrom
graycyrus:fix/memory-sync-indicators

Conversation

@graycyrus
Copy link
Copy Markdown
Contributor

@graycyrus graycyrus commented Jun 3, 2026

Summary

Fixes the unreliable per-source "Syncing…" indicators and the live "N syncing / N synced" counters on the Intelligence memory sources panel (MemorySourcesRegistry). Previously only one source could show as syncing at a time, intermediate stages never lit up the per-source bar, and progress got stuck at a placeholder.

Closes #3295.

Root causes addressed

  1. Only one source could show "syncing"MemorySourcesRegistry.tsx tracked a single syncingId: string | null. Now a syncingIds: Set<string> so concurrent syncs each render their own progress (immutable Set updates).
  2. Intermediate stages carried the wrong idMemorySyncStageBridge (src/openhuman/memory/sync.rs) emitted stored/queued with no connection_id and ingesting with connection_id = document_id, so the frontend (matching by connection_id === source.id) never lit up intermediate stages. Added a new optional source_id: Option<String> field to MemorySyncStageChanged (distinct from connection_id, which still carries the ingest-pipeline document_id). The bridge extracts the originating memory-source id from the mem_src:<source_id>:<item_id> chunk encoding (first-colon split, since item ids may be URLs). Frontend matches on source_id ?? connection_id (backward compatible).
  3. Brittle progress parsingparseSyncProgress now tolerates non-N/M detail strings and falls back to per-stage default percentages so the bar always advances.
  4. Event/poll races — the 5s poll reconciles missed events to a done state; documented the minor remount gap.

Out of scope (intentional, tracked separately)

  • Root cause Feat/landing revamp #3 (two accounts of the same Composio toolkit collapsing into one counter row) — requires reworking Composio chunk tagging with data-migration implications; deferred to a follow-up.
  • Root cause sync branches #5 full recovery — no new memory_sources_active_syncs RPC; rely on the existing 5s poll to rehydrate after a remount.

Event payload change

MemorySyncStageChanged gains source_id: Option<String> (the memory-source row id; None for channel-level syncs). connection_id semantics are unchanged. Serialized into the memory-sync-stage socket payload.

Tests

  • Rust (src/openhuman/memory/sync.rs): extract_mem_src_id unit tests (incl. URL item ids with colons) + MemorySyncStageBridge tests asserting source_id is populated for mem_src: syncs and None otherwise, with connection_id left unchanged. 10 lib tests pass.
  • Vitest (app/src/components/intelligence/__tests__/MemorySourcesRegistry.test.tsx): concurrent syncs render both rows, completed/failed clears only the right row, source_id preferred over connection_id, and tolerant parseSyncProgress cases. 11 tests pass.

Checks

pnpm typecheck, pnpm lint (0 errors), pnpm format:check, pnpm build, cargo check, the new Vitest suite, and the memory::sync Rust lib tests all pass. The full pnpm test:rust e2e suite and pnpm dev:app were not run locally due to a disk-space limit in the dev environment (ld: errno=28); the library tests covering this change pass.

Notes for reviewers

  • connection_id must keep carrying document_id for ingest-pipeline identity — this PR adds source_id rather than repurposing connection_id. Please sanity-check no downstream consumer depends on connection_id being the source id.

Summary by CodeRabbit

  • New Features

    • Concurrent memory-source syncs; persistent per-source result chips (items synced / up-to-date / failed); progress now driven by stage events (toasts shown on terminal events).
  • Documentation

    • Added detailed memory-source sync progress/status documentation and UI behavior notes.
  • Localization

    • Added translation strings for sync status in multiple languages.
  • Tests

    • Added unit and integration tests for progress parsing, event matching, UI state transitions, and result-chip behavior.

graycyrus added 2 commits June 3, 2026 19:24
Per-source "Syncing…" indicators and the live "N syncing / N synced"
counters on the Intelligence memory sources panel were unreliable: only
one source could show as syncing at a time, intermediate stages never lit
up the per-source bar, and progress got stuck at a placeholder.

- Track syncing sources as a Set<string> (syncingIds) instead of a single
  syncingId so concurrent syncs each render their own progress.
- Add a new optional source_id field to MemorySyncStageChanged (distinct
  from connection_id, which still carries the ingest-pipeline document_id).
  MemorySyncStageBridge extracts the originating memory-source id from the
  mem_src:<source_id>:<item_id> chunk encoding (first-colon split, since
  item ids may be URLs) so stored/queued/ingesting stages reach the right
  row. Frontend matches on source_id ?? connection_id (backward compatible).
- Make parseSyncProgress tolerant and add per-stage fallback percentages so
  the bar always advances; the 5s poll reconciles missed events to done.
- Tests: Rust bridge/source_id-mapping unit tests + Vitest concurrent-sync
  and tolerant-parsing coverage.

Composio same-toolkit counter collapse and remount state recovery are
intentionally out of scope (tracked separately).

Closes tinyhumansai#3295
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 3, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4eda789b-6811-459b-a85a-44513b3f592c

📥 Commits

Reviewing files that changed from the base of the PR and between 9b67b84 and 194b6cf.

📒 Files selected for processing (1)
  • app/src/components/intelligence/__tests__/MemoryWorkspace.test.tsx

📝 Walkthrough

Walkthrough

This PR implements reliable per-source memory sync indicators by extending the event bus to carry source_id through all stages, refactoring the frontend registry to track concurrent syncs per source with persistent terminal outcomes, and adding comprehensive test coverage and i18n translations.

Changes

Memory Source Sync Indicators

Layer / File(s) Summary
Backend event contract and socket.io bridge
src/core/event_bus/events.rs, src/core/socketio.rs
DomainEvent::MemorySyncStageChanged gains optional source_id field; socket.io bridge includes it in memory:sync_stage JSON payload for frontend row matching.
MemorySyncStageBridge source ID extraction and propagation
src/openhuman/memory/sync.rs
New extract_mem_src_id helper parses mem_src:<source_id>:<item_id> composite IDs; MemorySyncStageBridge derives and emits source_id for DocumentCanonicalized (stored/queued) and MemoryIngestionStarted (ingesting) events; unit and integration tests validate extraction and bridge behavior.
Source-level emit_sync_stage calls
src/openhuman/memory/ops/sync.rs, src/openhuman/memory_sources/sync.rs, src/openhuman/memory_sync/sources/github.rs, src/openhuman/memory/sync_pipeline_e2e_tests.rs
All emit_sync_stage invocations updated to pass source_id: channel-level syncs pass None, source-scoped syncs pass Some(&source.id), error path adds observability::report_error_or_expected for failure tracking.
Frontend per-source sync state management
app/src/components/intelligence/MemorySourcesRegistry.tsx (core logic)
Changes from single syncingId to concurrent syncingIds Set; adds per-source syncResults map for persistent terminal success/failed/up-to-date chips; event handler matches rows by source_id (preferred) or connection_id, parses progress with stage fallbacks, stores/clears terminal results with item counts or failure reasons, and handles manual sync with RPC rejection fallback.
Frontend sync progress parsing utilities
app/src/components/intelligence/MemorySourcesRegistry.tsx (exports)
Exports STAGE_FALLBACK_PERCENT per-stage baseline percentages, stage-aware parseSyncProgress(detail, stage?), and parseIngestedCount(detail) for extracting completed item counts.
Frontend UI rendering and terminal result display
app/src/components/intelligence/MemorySourcesRegistry.tsx (rendering)
Updates SourceRow to display persistent terminal chip (success/failed/up-to-date) when not syncing; progress bar width uses stage fallback percentages; adds CheckIcon and WarnIcon SVG components; footer text renders only when no progress and no result chip.
Comprehensive test coverage
app/src/components/intelligence/__tests__/MemorySourcesRegistry.sync.test.tsx
Unit tests for parseSyncProgress (numeric ratios, stage fallbacks, completed, edge cases) and parseIngestedCount (count extraction). Integration tests verify concurrent syncing (RC#1), event-to-row matching rules (RC#2), terminal result chip rendering (counts, up-to-date, failure reasons, clearing on new sync), and RPC rejection handling.
Documentation and i18n translations
.claude/memory.md, app/src/lib/i18n/{ar,bn,de,en,es,fr,hi,id,it,ko,pl,pt,ru,zh-CN}.ts
Architecture documentation in .claude/memory.md detailing sync ID encoding, MemorySyncStageBridge derivation, terminal state driving, and error reporting. Translation keys added across all 14 languages for sync result labels (completeTitle, itemsSynced, upToDate, failedLabel).

Sequence Diagram

sequenceDiagram
  participant User as User (UI)
  participant FrontendRegistry as MemorySourcesRegistry
  participant SyncService as memorySourcesService
  participant BackendSync as Backend (sync.rs)
  participant EventBus as DomainEvent Bus
  participant SocketIO as Socket.io Bridge
  participant FrontendListener as openhuman:memory-sync-stage Listener

  User->>FrontendRegistry: Click Sync on Source A
  FrontendRegistry->>FrontendRegistry: Add to syncingIds, clear result
  FrontendRegistry->>SyncService: syncMemorySource(source.id)
  SyncService->>BackendSync: spawn sync task with source_id
  BackendSync->>EventBus: emit_sync_stage("requested", source_id=A)
  EventBus->>SocketIO: MemorySyncStageChanged{stage, source_id}
  SocketIO->>FrontendListener: memory:sync_stage{stage, source_id=A}
  FrontendListener->>FrontendRegistry: Update syncProgress[A], show spinner
  BackendSync->>EventBus: emit_sync_stage("fetching", source_id=A)
  EventBus->>SocketIO: MemorySyncStageChanged
  SocketIO->>FrontendListener: memory:sync_stage{stage, source_id=A}
  FrontendListener->>FrontendRegistry: Update progress via parseSyncProgress
  BackendSync->>EventBus: emit_sync_stage("completed", detail="ingested 42 items", source_id=A)
  EventBus->>SocketIO: MemorySyncStageChanged
  SocketIO->>FrontendListener: memory:sync_stage{stage, source_id=A, detail}
  FrontendListener->>FrontendRegistry: Remove from syncingIds, store SyncResult{itemsCount=42}
  FrontendRegistry->>FrontendRegistry: Clear syncProgress[A], show "42 items synced" chip
  FrontendRegistry->>User: Display terminal result chip
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

  • tinyhumansai/openhuman#3381: Both PRs interact with the Notion ingest pipeline in src/openhuman/memory_sync/composio/providers/notion/ingest.rs, specifically around document rendering and markdown cleanup steps before memory tree ingestion.

Suggested labels

memory, rust-core

Poem

🐰 The rabbit hops through sync events with glee,
Now every source can dance concurrently!
With stage fallbacks and terminal chips so bright,
The progress bar finally shines just right. ✨
No more lost events in the socket night—
Per-source tracking's here, and it's pure delight! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(memory-sync): reliable per-source sync indicators and counters' accurately summarizes the main change: fixing unreliable per-source sync indicators and counters by implementing concurrent sync tracking, proper source_id threading, and robust progress parsing.
Linked Issues check ✅ Passed The PR meets all acceptance criteria from #3295: concurrent syncs now show via Set tracking, intermediate stages map to correct sources via source_id threading, live counters are accurate, progress is tolerant and reconciled via polling, and comprehensive tests for bridge mapping and concurrent-sync UI state are included.
Out of Scope Changes check ✅ Passed All changes are in scope for #3295: documentation updates, sync progress refactoring, i18n translations, event-bus updates, and bridge/RPC threading of source_id. Out-of-scope items (Composio toolkit collapse, dedicated remount RPC) are deferred as noted in PR objectives.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@CodeGhost21 CodeGhost21 left a comment

Choose a reason for hiding this comment

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

Review withdrawn — PR is in draft. Will re-post once it's ready for review.

graycyrus added 2 commits June 4, 2026 21:06
…icators

# Conflicts:
#	.claude/memory.md
#	app/src/components/intelligence/MemorySourcesRegistry.tsx
#	app/src/components/intelligence/__tests__/MemorySourcesRegistry.test.tsx
#	src/openhuman/memory_sync/sources/github.rs
…urce (tinyhumansai#3295)

A source sync that succeeded with 0 new items left no trace — the progress
bar cleared on `completed` and nothing replaced it, so it read as a failure.
The success toast also fired on the 4ms RPC ack (which only spawns the
background sync), not on the real outcome.

Drive the three states off the terminal stage event instead of the RPC ack:

- Success -> persistent row chip "N items synced" (or "Up to date" when 0 new),
  parsed from the `completed` detail ("ingested N item(s)"). Toast moved here
  from the RPC ack so it reflects the actual result.
- Failure -> persistent "Failed: <reason>" chip from the `failed` detail + an
  error toast. Internal failures are reported to Sentry in the core via
  `observability::report_error_or_expected` (known-expected auth/network/
  rate-limit errors are logged-not-reported; never-suppress honored).
- In-progress bar stays bound to non-terminal stage events; `isSyncing` now
  tracks real progress (syncingIds || syncProgress) and the misleading
  finally-clear of the syncing flag was removed.

Adds parseIngestedCount + 7 tests (count parse, success/upToDate/failure chips,
result-clear on re-sync, RPC-reject path). i18n keys added to all 14 locales.
@graycyrus graycyrus marked this pull request as ready for review June 5, 2026 05:46
@graycyrus graycyrus requested a review from a team June 5, 2026 05:46
@coderabbitai coderabbitai Bot added rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. memory Memory store, memory tree, recall, summarization, and embeddings in src/openhuman/memory/. labels Jun 5, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 5, 2026
…ent-driven toast (tinyhumansai#3295)

The per-source Sync button test asserted a success toast on the RPC ack, which
was the misleading pre-tinyhumansai#3295 behavior now removed. Success feedback fires on the
terminal `completed` stage event instead, so the test emits that event before
asserting the toast.
@graycyrus
Copy link
Copy Markdown
Contributor Author

@enamakel @sanil-23 if any one of you can review and approve

@graycyrus graycyrus requested review from sanil-23 and senamakel June 5, 2026 07:10
@graycyrus graycyrus merged commit ec5c26c into tinyhumansai:main Jun 5, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

memory Memory store, memory tree, recall, summarization, and embeddings in src/openhuman/memory/. rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Per-source memory sync indicators and live syncing/synced counters are unreliable

2 participants