@@ -259,6 +259,83 @@ def test_recent_activity_format_project_output_no_results():
259259 assert "No recent activity found" in out
260260
261261
262+ def test_recent_activity_format_project_output_renders_all_entities_and_relations ():
263+ """Regression for #784: the formatter must render every row the API returned.
264+ Previously the body was hardcoded to `[:5]` while the heading reported the
265+ true total — a result set of N>5 entities would show 5 rows under a heading
266+ that claimed N, with no signal the body was truncated. `page_size` is now
267+ the only knob; heading count and body row count must always agree.
268+ """
269+ import importlib
270+
271+ from basic_memory .schemas .memory import RelationSummary
272+
273+ recent_activity_module = importlib .import_module ("basic_memory.mcp.tools.recent_activity" )
274+ now = datetime .now (timezone .utc )
275+
276+ # Counts chosen to comfortably exceed the old hardcoded `[:5]` slice and any
277+ # plausible reintroduced default cap.
278+ entity_titles = [f"Entity { i } " for i in range (15 )]
279+ relation_titles = [f"Relation { i } " for i in range (12 )]
280+
281+ results = [
282+ ContextResult (
283+ primary_result = EntitySummary (
284+ external_id = f"550e8400-e29b-41d4-a716-44665544{ i :04d} " ,
285+ entity_id = i ,
286+ permalink = f"notes/entity-{ i } " ,
287+ title = title ,
288+ content = None ,
289+ file_path = f"notes/entity-{ i } .md" ,
290+ created_at = now ,
291+ ),
292+ observations = [],
293+ related_results = [],
294+ )
295+ for i , title in enumerate (entity_titles )
296+ ] + [
297+ ContextResult (
298+ primary_result = RelationSummary (
299+ relation_id = 100 + i ,
300+ entity_id = i ,
301+ title = title ,
302+ file_path = f"notes/entity-{ i } .md" ,
303+ permalink = f"notes/entity-{ i } " ,
304+ relation_type = "references" ,
305+ from_entity = f"Entity { i } " ,
306+ to_entity = f"Entity { i + 1 } " ,
307+ created_at = now ,
308+ ),
309+ observations = [],
310+ related_results = [],
311+ )
312+ for i , title in enumerate (relation_titles )
313+ ]
314+
315+ activity = GraphContext (
316+ results = results ,
317+ metadata = MemoryMetadata (depth = 1 , generated_at = now ),
318+ )
319+
320+ out = recent_activity_module ._format_project_output (
321+ project_name = "proj" ,
322+ activity_data = activity ,
323+ timeframe = "7d" ,
324+ type_filter = ["entity" , "relation" ],
325+ page = 1 ,
326+ )
327+
328+ for title in entity_titles :
329+ assert title in out , f"Entity { title !r} missing from formatter output"
330+ for i in range (len (relation_titles )):
331+ assert f"[[Entity { i } ]] → references → [[Entity { i + 1 } ]]" in out , (
332+ f"Relation { i } missing from formatter output"
333+ )
334+ # Heading total matches the body — no silent truncation.
335+ assert f"Recent Notes & Documents ({ len (entity_titles )} )" in out
336+ assert f"Recent Connections ({ len (relation_titles )} )" in out
337+
338+
262339def test_recent_activity_format_project_output_includes_observation_truncation ():
263340 import importlib
264341
0 commit comments