Conversation Feed
Plan Metadata
- Plan type:
plan - Parent plan:
N/A - Depends on:
N/A shared-lambda-interface.md- Status:
documentation
Status semantics: - draft: Plan is being created or updated and is not final. - approved: Plan is approved but not yet applied in code. - documentation: Code currently exists and matches the plan contract.
Update rule: - When an existing plan is edited, set status to draft until re-approved.
System Intent
- What is being built: Architecture-level contract for serving paginated conversation summaries to the mobile side menu.
- Primary consumer(s): Mobile memories screen side menu and the conversations feed API surface.
- Boundary (black-box scope only):
/conversations/feedrequest/response behavior, pagination semantics, and UI integration outcomes.
Stage Gate Tracker
- [x] Stage 1 Mermaid approved
- [x] Stage 2 I/O contracts approved
- [x] Stage 3 pseudocode/technical details approved or skipped
1. Mermaid Diagram
Reference: .agent/skills/create-mermaid-diagram/SKILL.md
flowchart TD
A[Mobile Side Menu - main/app/app/index.tsx + main/app/components/side-menu.tsx] -->|feed request cursor limit| B[Conversation Feed Client - main/app/lib/api/conversations]
B -->|POST /conversations/feed| C[Conversations Feed Lambda - main/server/api/conversations/feed/app.py]
C -->|conversation list and next_cursor| B
B -->|paged feed state and errors| A
classDef unchanged fill:#d3d3d3,stroke:#666,stroke-width:1px;
classDef updated fill:#ffe58a,stroke:#666,stroke-width:1px;
classDef deleted fill:#f4a6a6,stroke:#666,stroke-width:1px;
classDef created fill:#a8e6a3,stroke:#666,stroke-width:1px;
class A,B,C unchanged; 2. Black-Box Inputs and Outputs
Keep this short. Define types in JSON-style blocks and capture each flow with path-level rows. - Flow naming rule: each ### Flow heading must follow file.function, test format. - Use this exact shape: ### Flow: `<path/to/file.ext>.<functionName>`, `<path/to/test_file.ext>`.
Global Types
Define shared types used across multiple flows. Pagination semantics (cursor, limit, defaults, max range) follow the canonical contract in memory-feed.md.
ConversationFeedRequest {
cursor?: string | null
limit?: number (int, 1..50, default=20)
}
ConversationSummary {
id: string
title: string
last_message: string
updated_at: string (ISO-8601 timestamp)
}
ConversationFeedResponse {
conversations: ConversationSummary[]
next_cursor?: string | null
}
ConversationFeedUiState {
conversations: ConversationSummary[]
has_more: boolean
is_loading: boolean
error_message?: string
}
StandardError {
status?: number
code?: string
message: string
}
Flow: main/app/app/index.tsx.MemoriesScreen, main/app/__tests__/memory-feed-screen.test.tsx
Type Definitions
ConversationMenuIntegrationInput {
request: ConversationFeedRequest
user_action?: "open_menu" | "load_more" | "open_conversation"
selected_conversation?: ConversationSummary
}
ConversationMenuIntegrationOutput {
ui_state: ConversationFeedUiState
navigation_target?: "/chat"
navigation_query?: string
}
Paths
| path-name | input | output/expected state change | path-type | notes | updated |
|---|---|---|---|---|---|
conversation-menu.initial-load | ConversationMenuIntegrationInput request with empty cursor | ConversationMenuIntegrationOutput ui_state.conversations populated | happy path | menu shows first page and load-more state from next_cursor | |
conversation-menu.load-more | ConversationMenuIntegrationInput user_action=load_more with prior next_cursor | ConversationMenuIntegrationOutput ui_state.conversations appended | happy path | next page merges into existing list | |
conversation-menu.open-conversation | ConversationMenuIntegrationInput user_action=open_conversation | ConversationMenuIntegrationOutput navigation_target=/chat navigation_query=<selected_conversation.title> | happy path | selecting a row navigates to chat | |
conversation-menu.request-failure | ConversationMenuIntegrationInput where feed request fails | ConversationMenuIntegrationOutput ui_state.error_message set | error | error state is rendered in side menu |
Flow: main/server/api/conversations/feed/app.py.implementation, main/server/tests/unit/test_conversations_feed_app.py
Type Definitions
ConversationsFeedLambdaInput = ConversationFeedRequest
ConversationsFeedLambdaOutput = ConversationFeedResponse
Paths
| path-name | input | output/expected state change | path-type | notes | updated |
|---|---|---|---|---|---|
conversations-feed.page-success | ConversationsFeedLambdaInput with valid cursor and limit | ConversationsFeedLambdaOutput with conversations[] and optional next_cursor | happy path | returns one deterministic page from current mock dataset | |
conversations-feed.end-of-feed | ConversationsFeedLambdaInput with cursor at final page | ConversationsFeedLambdaOutput next_cursor=null | subpath | signals no more pages | |
conversations-feed.limit-out-of-range | ConversationsFeedLambdaInput limit<=0 or limit>50 | StandardError status=500 message=Invalid limit | error | numeric limit range violations are rejected | |
conversations-feed.cursor-negative | ConversationsFeedLambdaInput cursor<0 | StandardError status=500 message=Invalid cursor | error | negative cursor is rejected | |
conversations-feed.cursor-parse-fallback | ConversationsFeedLambdaInput cursor non-numeric | ConversationsFeedLambdaOutput as if cursor=0 | subpath | parse failures are normalized to first page, not rejected | |
conversations-feed.limit-parse-fallback | ConversationsFeedLambdaInput limit non-numeric | ConversationsFeedLambdaOutput as if limit=20 | subpath | parse failures are normalized to default limit, not rejected |
3. Pseudocode / Technical Details for Critical Flows (Optional)
- Stage 3 decision: skipped.
- Reason: this plan intentionally stays at architecture and integration-flow level; no lower-level implementation detail is required.
4. Handoff to Related Plan Reconciliation
After all stages are approved, apply .agent/skills/reconcile-plans/SKILL.md to propagate contract updates across linked plans.