Memories Feed
An authenticated user opens the app's home screen and sees a paginated feed of their captured memory sessions, each with a thumbnail, timestamp, type label, and processing status. Segments from the same recording session are automatically grouped into a single feed item.
Flow
-
App loads home screen — The
MemoriesScreencomponent (main/app/app/index.tsx) callsuseMemoriesFeed({ limit: 20 })via React Query. This hook calls the memories-feed Lambda. -
Authentication — The Lambda requires a valid Cognito JWT (
require_auth_context=True). Theuser_idis extracted from the auth context. -
Database connection — The Lambda connects to PostgreSQL using credentials from SSM Parameter Store (or
DATABASE_URLin local dev) and initializes SQLAlchemy. -
Cursor parsing — A numeric
cursor(offset) andlimit(capped at 50, default 20) are parsed from the request payload. -
Query — All
WorldMMSegmentrecords for the authenticated user are queried, ordered bystart_time DESCthenid DESC. -
Session grouping — Segments are grouped by their
source_session_id. Segments without asource_session_idare each treated as their own session (keyed by segment ID). -
Sorting — Sessions are sorted by their latest segment's
start_time(newest first) to ensure a natural chronological ordering. -
Pagination — Pagination is applied at the session level:
OFFSET cursor LIMIT limit+1. This ensures that each feed item corresponds to exactly one session. -
Session aggregation — For each session, the feed derives:
- id: The session ID (either
source_session_idor a single segment's ID if ungrouped) - time: The earliest segment's
start_time(session start) - type: The richest segment type in the session (priority: visual > audio > text)
- thumbnail: The first visual segment's presigned S3 URL (TTL 1 hour), if any
-
processing_status: The overall session status (pending if any segment pending, failed if any segment failed, complete otherwise)
-
Next-cursor computation — If
limit+1sessions are returned, the next cursor iscursor + limit; otherwise there are no more pages. -
Response — Returns
{ "memories": [...], "next_cursor": "<offset-string>" | null }. Each memory item containsid,time,type,featured,processing_status, and optionallythumbnail. -
UI rendering — The
MemoryFeedcomponent renders the list. After a session finishes recording (recordingStatetransitions fromstoppingtoidle), the feed is automatically refetched to show newly ingested memories.
Entry Point
- Lambda:
main/server/api/memories/feed/app.py→lambda_handler - HTTP method:
GET /memories/feed(API Gateway, Cognito-authenticated) - Mobile:
main/app/lib/api/memory/useMemoryApi.ts
Dependencies
- PostgreSQL (
WorldMMSegmenttable) - S3 (
BUCKET_NAME) for presigned thumbnail URLs - Cognito JWT authorizer
Audio Type Support Gap
The feed correctly returns sessions whose type is "audio" (when no visual segment exists). However, the MemoryViewerModal (docs/docs/memory-viewer-modal.md) has no AudioPlayer component and no audio branch in its renderItem logic. Audio memories fall through to the thumbnail/placeholder path — users can see the session in the feed but cannot play back the audio. Backend infrastructure for audio playback endpoints is tracked in docs/bugs/2026-05-08-audio-metadata-endpoint-403-forbidden.md.
Key Changes from Segment-Level Feed
Previously, the feed returned one item per segment, which meant a multi-media recording session (audio + video) would appear as multiple separate feed items. Now:
- The feed groups segments by
source_session_idand returns one item per session - The session's
typereflects the richest content (visual takes priority over audio, which takes priority over text) - The session's
thumbnailis drawn from the first visual segment if available - The session's
processing_statusreflects the overall completion state across all segments - Pagination operates at the session level, not the segment level, ensuring consistent and predictable page boundaries