Skip to content

sqlite_compat.py relies on the deprecated stdlib datetime adapter (3 DeprecationWarnings in the suite) #260

Description

@cdeust

What

Three SQLite-backed tests emit Python's DeprecationWarning for the default
datetime adapter, which is scheduled for removal:

tests_py/handlers/test_consolidate.py::TestConsolidateHandler::test_with_memories
tests_py/handlers/test_consolidate.py::TestConsolidateHandler::test_protected_memories_skip_compression
tests_py/integration/test_memory_lifecycle.py::TestFullLifecycle::test_store_consolidate_recall
  mcp_server/infrastructure/sqlite_compat.py:287: DeprecationWarning: The default
  datetime adapter is deprecated as of Python 3.12; see the sqlite3 documentation
  for suggested replacement recipes
    cur = self._real.execute(translated, params)

Reproduction (2026-07-29, macOS, Python 3.12, worktree at c1f449f)

$ env -u DATABASE_URL CORTEX_MEMORY_STORE_BACKEND=sqlite python -m pytest -q
6367 passed, 81 skipped, 3 warnings, 117 subtests passed in 123.48s

The three warnings above are the three. sqlite_compat.py:287 passes a
datetime straight through to sqlite3.Connection.execute, so the stdlib's
deprecated implicit adapter converts it.

Why it is not a drive-by fix

sqlite3.register_adapter(datetime, ...) is a process-global side effect that
changes the ON-DISK text of every datetime column the SQLite store writes.
That is a persisted-format change (§13.1 E3): it needs a decision on the
serialization (ISO-8601 with or without timezone), a read path that accepts
both the old and the new spelling or a one-shot migration, and a test that
feeds old-data-in/new-code. Picking one silently inside an unrelated PR is
exactly the shim the project forbids.

Acceptance criteria

  1. mcp_server/infrastructure/sqlite_compat.py no longer relies on the
    implicit stdlib adapter: datetimes are serialized explicitly at the one
    boundary that writes them.
  2. The chosen wire format is stated in the module docstring, with the read
    path that parses it.
  3. Existing SQLite stores keep reading: a test writes a row in the pre-change
    spelling and asserts the post-change reader returns the same value (or a
    one-shot migration ships and is tested).
  4. The suite runs warning-free for this rule — a filterwarnings entry that
    turns this DeprecationWarning into an error, so it cannot silently return.

Provenance

Seen while running the standard suite during #253 (pyright/tree-sitter
resolution divergence). Outside that change's blast radius — #253 touches
core/ast_parser.py, core/ast_extractor_registry.py, the dependency pin
and CI/doc wiring, with no import path to infrastructure/sqlite_compat.py
so it is deferred here per coding-standards §14.3 rather than folded in.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions