Summary
search pagination is unstable across index mutations and the response carries no anchor that would let a client detect drift. Two paginated calls with the same query and limit can return different result sets if index runs in between, with no way for the caller to notice.
Evidence
src/CodeIndex/Database/DbSearchReader.cs:325 — limit-only ordering, no cursor field. The response also has no generation counter / snapshot timestamp.
- Reproducer:
cdidx search "foo" --limit 10 → record results A.
- Trigger
index to add 5 new matching files.
cdidx search "foo" --limit 10 → results B may differ from A even though the user asked for the same "first 10" twice.
Impact
- Agents that paginate through results in chunks see duplicates and gaps.
- No way to write a stable "show me page 2" UX; the underlying contract is undocumented.
Proposed direction
Two layered fixes:
- Detect drift: include a
result_stable_at timestamp (or a DB generation counter) in every search response. Clients comparing two responses can tell whether they're seeing a consistent slice.
- Allow stable pagination: accept an optional
cursor parameter that anchors on (rank, rowid) from the previous response. The handler then continues from that anchor rather than re-running the query from the top.
Document the contract in DEVELOPER_GUIDE.md.
Repro env
- Branch:
main @ 2ee912d (release v1.21.0)
Summary
searchpagination is unstable across index mutations and the response carries no anchor that would let a client detect drift. Two paginated calls with the same query and limit can return different result sets ifindexruns in between, with no way for the caller to notice.Evidence
src/CodeIndex/Database/DbSearchReader.cs:325— limit-only ordering, no cursor field. The response also has no generation counter / snapshot timestamp.cdidx search "foo" --limit 10→ record results A.indexto add 5 new matching files.cdidx search "foo" --limit 10→ results B may differ from A even though the user asked for the same "first 10" twice.Impact
Proposed direction
Two layered fixes:
result_stable_attimestamp (or a DB generation counter) in every search response. Clients comparing two responses can tell whether they're seeing a consistent slice.cursorparameter that anchors on(rank, rowid)from the previous response. The handler then continues from that anchor rather than re-running the query from the top.Document the contract in
DEVELOPER_GUIDE.md.Repro env
main@ 2ee912d (release v1.21.0)