Memory Feed Session Grouping
Status: Complete Date: 2026-04-28
Problem
The memory feed was returning one item per segment, not per recording session. When a user captured audio and video in a single session, they would see two separate feed items instead of one unified memory.
Solution
Modified the /memories/feed Lambda endpoint to:
- Group segments by
source_session_id— All segments with the same session ID are merged into a single feed item - Aggregate at session level — Each feed item represents one complete session with aggregated properties:
- id: The session ID (or segment ID if ungrouped)
- time: Session start time (earliest segment start_time)
- type: Richest type across all segments (visual > audio > text)
- thumbnail: First visual segment's thumbnail, if any
- processing_status: Overall status (pending if any segment pending, failed if any failed, complete otherwise)
- Paginate at session level — The
cursorandlimitnow apply to sessions, not segments
Implementation Details
Backend Changes
File: main/server/api/memories/feed/app.py
- Added
_derive_richest_type(segments)to select the best type across multiple segments - Added
_get_richest_frame_key(segments)to find the first visual segment's frame key - Added
_get_overall_processing_status(segments)to compute aggregate status - Added
_group_segments_by_session(segments)to organize segments by session ID - Modified
implementation()to: - Query all user segments (removed offset/limit from query)
- Group segments into sessions
- Sort sessions by latest segment start_time (newest first)
- Apply pagination at the session level
- Build one feed item per session with aggregated properties
Testing
New tests: main/server/tests/integration/test_memories_feed_session_grouping.py
Added comprehensive tests validating: - Single segments become single feed items - Multiple segments with same session ID are grouped - Session type reflects richest segment type - Session thumbnail comes from visual segments - Session processing_status is pending if any segment is pending - Pagination is applied at session level - Session start_time is the earliest segment - Sessions are ordered newest-first
Existing tests: main/server/tests/integration/test_memories_feed_pagination.py
All 14 existing tests pass unchanged because: - Test fixture seeds segments without explicit source_session_id - Each segment is treated as its own session (keyed by segment ID) - Old test expectations (e.g., seg-a-0, seg-a-1) still work because segment IDs become session IDs
Documentation
File: docs/docs/memories-feed.md
Updated to reflect: - Feed now returns sessions, not segments - Grouping logic and session-level aggregation - Type, thumbnail, and status derivation rules - Session-level pagination - Key changes section explaining the shift from segment-level to session-level
Backwards Compatibility
The feed response structure ({ memories: [...], next_cursor }) and memory item schema (id, time, type, thumbnail, processing_status) remain unchanged. The semantic meaning of id shifts from segment ID to session ID, which is transparent to the frontend.
Frontend impact: None required. The MemoryFeed.tsx component treats id as an opaque identifier and works identically with session IDs or segment IDs.
Testing Results
- All 14 existing pagination tests pass
- All 8 new session grouping tests pass
- Total: 22 tests, 100% pass rate
Future Work
Possible enhancements: 1. Add a segment_count field to each feed item to indicate how many segments comprise the session 2. Add segment-level access via a new /memories/{id}/segments endpoint to inspect individual segments within a session 3. Consider storing the session-level aggregation in the database (denormalization) if query performance becomes an issue