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)
-
title: Not currently propagated by the feed lambda — the
titlefield is absent from the response payload.MemoryFeedItem.titleis declared optional (title?: string | null) in the TypeScript type to accommodate future inclusion. -
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. Thetitlefield is not yet included in the server response; the client TypeScript type declares it optional to anticipate future inclusion. -
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
MemoryCard Title Display
MemoryCard (main/app/components/memory/MemoryCard.tsx) renders a title overlay at the bottom of each thumbnail tile:
When memory.title is null, undefined, or an empty string, the component falls back to the static string "Untitled Memory". The title is rendered in a semi-transparent black bar (rgba(0,0,0,0.7)) pinned to the bottom of the card using position: absolute. Because the feed lambda does not currently send title in the response, all cards currently display "Untitled Memory" until the feed propagation gap is resolved.
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