Repository navigation
feat(memory): add bounded cursor pagination for Memory entries #1656
Description
Feature description
Add an authorized, bounded Server query for Memory entries, implementing the directory portion of D2 in RFC #1455. Track this as a focused deliverable related to the broader capacity problem in #1321.
Problem and proposed solution
At aa697c5315204249e090acdf1a00d0582e851c5f, POST /v1/memory/entries/list accepts Scope, inactive-entry, and tag-filter inputs but has no page size or cursor. Its response contains the complete selected entries. The persistence implementation loads entry versions referenced by the complete Memory manifest.
Client-side pagination or slicing the fully assembled HTTP response leaves the Server work unbounded. Generic Artifact pagination also does not paginate entries inside one Memory Artifact.
Specify and implement a bounded query with an explicit default/maximum page size, deterministic ordering, an opaque continuation cursor, and clear consistency/error semantics. Agree on whether this is an additive operation or a versioned evolution; do not silently change existing clients from complete results to an undisclosed first page.
Acceptance criteria
- Publish request/response schemas, ordering, page-size bounds, end-of-list behavior, and cursor invalidation/expiry rules.
- Apply current authorization and supported inactive/tag filtering before page selection; continuation requests reauthorize access.
- Bind continuation to the relevant resource/query context. Reject tampered, mismatched, and invalid/expired cursors without revealing other Scopes or private data.
- Define concurrent append/revise/retire behavior: traverse a pinned revision or explicitly require restart when the consistency boundary changes. Do not silently skip or duplicate entries.
- Preserve exact Memory and entry/citation identities so a listed item can be inspected correctly.
- Bound entry-body loading and response assembly, not just the final response slice. State and measure any remaining whole-manifest cost; if it prevents the promised bound, agree the necessary storage/index work before claiming bounded browsing.
- Cover empty and multi-page results, the final page, inactive/tag filters, unauthorized requests, permission changes, invalid cursors, and concurrent mutations through observable behavior tests.
- Include a scale fixture or measurement demonstrating the declared bounds without freezing private call counts.
- Document compatibility/migration for existing clients; keep old full-list semantics explicit while supported. Regenerate OpenAPI-derived code and run contract/relevant behavior checks.
Alternatives considered
- Fetch everything and page in the UI: does not bound Server work or network payload.
- Truncate responses without continuation: loses data and misrepresents completeness.
- Reuse Artifact-directory pagination: addresses a different level of the data model.
Additional context
- Refs bug: Memory append storage and latency grow superlinearly with entry history #1321, docs(rfc): propose Tauri desktop control center #1455 (D2/AC-19), Tracking Issue: deliver a secure PowerContext desktop control center #1428, and the Web viewer work in feat(web): add a read-only Memory entry viewer #1454.
- Current request/response contract.
- Current entry loading.
- Scope: entry directory queries. Memory history pagination, full storage-growth remediation, split/compaction policy, and Desktop UI are separate work.
- Open for community implementation in a focused PR targeting
master. Coordinate shared cursor/consistency and compatibility choices with the history work. - This gates complete Memory browsing, not Desktop's initial explicit-save/search/exact-detail workflow.
Desktop consumer: #1654.
Related history-pagination deliverable: #1657. Both issues cover focused portions of #1321 and should agree on shared consistency/compatibility decisions.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsIn progress
Activity
wutongyuonce commentedon Sep 22, 2026
Coordinated pagination SPEC proposal for #1656 and #1657
@knqiufan I would like to implement #1656 first and #1657 second as independently reviewable PRs. I reviewed the existing cursor, authentication, OpenAPI, and migration patterns; those established conventions do not need separate maintainer decisions.
Shared public contract
limit: default50, maximum100. A documented response-byte budget may end a page earlier and return continuation.next_cursormeans completion. Tampered, expired, cross-Scope, cross-endpoint, or query-mismatched cursors fail without disclosing private data.#1656 entry query
limitand optionalcursor.entry_id ASC; legacy full-list behavior remains unchanged.#1657 history query
since_revisionsemantics, including0, omitted/null, and positive nonexistent revisions.(revision ASC, change_ordinal ASC).Required persistence boundary
The current entry path decodes the complete manifest, and the current history path loads Artifact revisions before filtering. HTTP-level slicing would not satisfy either Issue.
I propose:
valid_from_revision/valid_to_revision, or an equivalent representation), permitting keyset reads at the pinned revision without copying a complete manifest per revision.(scope, memory_artifact_id, revision, change_ordinal), permitting continuation inside a large revision.Verification and PR split
#1656 will cover the additive entries contract, shared cursor context, directory index/backfill, generated code, observable behavior tests, and a scale measurement based on loaded rows/items and serialized bytes. #1657 will reuse that seam and add change-row/backfill support plus large-single-revision coverage.
The existing repository already determines cursor implementation, authorization, migration workflow shape, generated-code handling, and the two-PR split. The only coordination decision is whether the additive pinned-revision contract and normalized query-index boundary above are acceptable. Please confirm that boundary; unless maintainers identify a conflict, I will apply the existing conventions directly rather than ask for further implementation choices.
knqiufan commentedon Sep 27, 2026
@wutongyuonce Thanks for coordinating both issues. I support additive paged operations with a pinned Memory revision and rebuildable query indexes, keeping the existing full-result operations unchanged. Separate PRs for #1656 and #1657 make sense.
Before freezing the contract, please clarify three points:
The normalized-index approach is acceptable in principle, provided authoritative records and exact historical references remain preserved. Shared decisions can stay in this issue and be reused by #1657.
wutongyuonce commentedon Sep 27, 2026
@knqiufan Thanks. I rechecked these points against the current tag transaction, cursor/error, migration, exact-detail, and #1709 projection boundaries. I propose the following contract refinements.
1. Tag-filtered continuation
A Memory revision alone cannot snapshot mutable tag assignments, so tag-filtered pagination will use a separate durable tag generation owned by the tag persistence authority. It will not reuse
governance_generationor reinterpret a Memory revision.tag_state_changedreason; the client restarts from page one.This gives the pinned-revision guarantee for Memory state while explicitly making tag changes a restart boundary, so the contract does not imply a tag snapshot that the current model cannot provide.
2. Existing databases while backfill is incomplete
The migration is feature-scoped rather than a Server-wide startup gate.
plan/apply/verifybackfill in bounded batches for SQLite and OceanBase. The procedure requires quiesced writes and rejects mixed old/new writers.503 memory_query_index_unavailable, after normal authentication and authorization. They never fall back to the old unbounded read path.This keeps an optional new read capability from taking down unrelated Server behavior while still failing closed for the bounded-work claim.
3. Page bytes, oversized items, and scale evidence
I will narrow the new #1656 operation to a directory response. Each item contains the exact citation (
memory_ref,entry_id,entry_version_id), logical version, kind, and state. It does not expandtext,source_refs, orartifact_refs; callers use the existing exact-detail operation for the body. The legacy full-list response remains unchanged.50, maximum100.4 MiBencoded item-payload budget, consistent with the existing bounded Source listing pattern, plus a separately bounded response envelope/cursor.413 memory_directory_item_too_large, with no partial page and no advanced cursor. Entry-body size does not affect directory traversal.Scale evidence will cover 200, 1,000, and 5,000-entry fixtures on SQLite and OceanBase where available, recording:
limit + bounded lookahead, independent of total directory/history size;The normalized index remains rebuildable query state. Artifact Revisions, Memory manifests, entry versions, tag rows, and exact historical references remain authoritative.
With these refinements, the shared contract remains additive and pinned by Memory revision, tag-filtered cursors have an explicit restart boundary, incomplete migration cannot masquerade as bounded browsing, and both response assembly and persistence work have measurable bounds. If this resolves the three points, I will implement #1656 first and reuse these shared rules in the separate #1657 PR.
PsiACE commentedon Oct 1, 2026
Note: We'll revisit next steps as the migration and Memory redesign become clearer; see this comment.