Files
deepseek-harness/docs/rfc/implemented/feature/2026-07-10-session-query-service.md

4.7 KiB

RFC: Exact session query service

Status: implemented

Problem

Session history exists in two places: current SessionStore objects and an optional persistence backend. Consumers that need exact inspection would otherwise duplicate live-versus-persisted precedence, persistence lifecycle handling, raw-event surface classification, and defensive cloning. Durable state can lag the live log between checkpoints, so persistence alone is not a truthful current source.

Full-text search is related but materially larger. Putting provider coordination, synchronization, invalidation, ranking, and cursor state into the exact-read service would create a second state machine beside the concrete database owner.

Decision

@deepseek-ai/dsh-session-query owns ctx.sessionQuery, a small trusted exact-read service over one logical corpus. It exposes listSessions(), provider-independent filterSessions(filters), listEvents(sessionId), filterEvents(sessionId, filters), and bounded readEvent(request). The package also declares the separate abstract ctx.sessionSearch contract and shared semantic extraction used by the SQLite search decision, but ctx.sessionQuery does not coordinate providers or synchronize a derived index.

The service observes the optional ctx.sessionPersistence binding dynamically but retains no persisted cache or invalidation listener. Each cross-corpus list asks the active backend for authoritative metadata, then overlays a fresh live-store list. Matching ids become one SessionRecord: the live header wins and live/persisted independently report source availability. Immutable header disagreement is SESSION_QUERY_SOURCE_CONFLICT.

An exact target read first checks the live store and snapshots the live header and event log. This path never consults persistence, so a failing durable backend cannot make known live history unreadable. With no live target, the service lists current persistence metadata, proves the id exists, loads it, and rejects a list/load header mismatch. All returned headers and events cross one structured-clone boundary.

Surface semantics

dsh-session exports foldSurface(events), and SurfaceManager uses the same transition functions for its incremental cache. The fold returns detached current nodes and each replacement's actual removed seqs. listEvents() uses that result to classify every raw event as current, shadowed, or log-only, so inspection cannot disagree with model-history derivation about positional replacement semantics.

readEvent() returns the complete target plus raw neighbors by contiguous seq. before and after default to zero and are independently bounded by readWindowMax, default 50. The result carries a cloned SessionHeader, not a source-availability record, because determining a live target's persisted flag would violate the guarantee that live exact reads do not depend on persistence health.

Security boundary

The service is context-wide trusted infrastructure, not an authorization layer. A future model-facing history tool or human UI applies explicit caller/session scope. This phase adds no model-facing tool and changes no transcript or snapshot surface.

Alternatives considered

  • Put logical-corpus resolution directly in every consumer — rejected because source precedence, conflicts, optional-service lifecycle, cloning, and surface classification are shared correctness rules.
  • Query only persistence — rejected because checkpoints can lag the current live log.
  • Cache persisted metadata and listen for writes/removals — rejected because exact reads can ask the authoritative sources directly, while cache invalidation adds lifecycle and concurrency state before scale requires it.
  • Put provider registration into the exact-read service — rejected because the SQLite package owns one reconciliation/transaction lifecycle; a registry would split that state without a second provider to justify it.
  • Include lineage and provenance traversal — rejected because canonical logs remain sufficient to add those higher-level views when a concrete consumer requires them.

Consequences

Exact reads have one source-resolution state variable: the currently mounted persistence service. There are no provider queues, fingerprints, extractor registries, observation generations, or derived index updates in ctx.sessionQuery. Exact reads and semantic scans remain usable in live-only deployments and deterministic when persistence is present.

Cross-corpus listing and persisted exact reads perform backend I/O on each call. That is deliberate: correctness comes from current authoritative state, while scale-oriented full-text search uses the separately owned SQLite derived index.