#692·memU

[DISCUSSION] file.top_k documents file count but actually bounds L2 segments

Author: wutongyuonceCreated Sep 3, 2026Updated Sep 3, 2026

Description

RetrieveFileConfig.top_k is documented as "Total number of files to retrieve", but the current retrieve pipeline never uses it to bound files.

In AgenticMixin.progressive_retrieve:

  • _recall_segments passes file.top_k to vector_search_segments, so top_k actually bounds L2 segments, not L1 files.
  • _collect_files then rolls those ≤top_k segments up to their files — the file list is a derived set, not a ranked, capped list.

Consequence: a single chatty recall file whose segments occupy all top_k slots yields exactly one file in the files layer, even though the config says "5 files". Diverse-but-weaker files are crowded out before they ever get a vote.

Motivation

The files layer is what the agent reads as "these are my relevant memory files". When one broad file (e.g. a long running-notes file with many lines) monopolizes the segment window, retrieval hides every other relevant file. This is user-visible: query the store, and the number of files returned is effectively unpredictable — it is distinct(recall_file_id) of whatever segments won, between 1 and top_k.

The file layer itself is intentionally not a second ranked search today (ADR 0007's roll-up design). The bug is that the only knob documented as file count instead silently means segment count.

Candidate directions (want input before implementing)

  1. Honest config, keep roll-up. Rename/redocument the knob to segment.top_k (or add file.enabled clarity) and accept that files count is derived. Smallest change, no behavior shift, but the documented promise stays wrong-ish.
  2. Enlarge the L2 window, keep roll-up. Search more segments than the file target (e.g. file_count × segments_per_file_guess, or a new file.candidates multiplier), then roll up and cut to top_k distinct files. Keeps "no second ranked search"; a chatty file can still dominate only within its own file's segments once candidates are wider. Requires a per-file segment cap or re-slicing change to truly bound one file's share.
  3. Rank files natively. Files already carry an embedding ("{name}: {description}" on commit). Add a real file-level ranked search with its own top_k, returning files first and segments as supporting evidence. Biggest change: new repo path (vector_search_files), possibly conflicts with ADR 0007's "L1 is not searched" stance, needs its own discussion.

No code yet — this is a semantics decision affecting the config contract and the inject result shape, so it should be settled here first.

Platform

Platform Independent

Priority

Minor (behavioral correctness of a documented knob, not a crash)

Out of scope

  • Segment-layer ranking quality (covered elsewhere)
  • Changing the roll-up scoring from max(segment score)