Skip to content

Make SMSStore.clear durable after SQLite close/reopen #114

Description

@Justinabox

Summary

SMSStore.clear() only deletes SQLite rows when the database connection is currently open. If a SQLite-backed store is closed and clear() is called before the next initialize(), the in-memory list is emptied but the persisted rows remain on disk and reappear after reopening.

That makes cleanup/reset flows look successful in process (count() == 0) while durable SMS history is not actually cleared.

Evidence

Affected code:

  • callstack/sms/store.py:159-164 supports closing and reopening a SQLite-backed store.
  • callstack/sms/store.py:259-266 clears memory/pending saves, but runs DELETE FROM messages only when self._db is not None.
  • Existing tests cover clear() before first initialize with no existing persisted rows, but not clear-after-close with existing persisted rows.

No-hardware probe on main (92e0f02042bb7496d7bca2fdd7f29a0625592672):

PYTHONPATH=. uv run --no-project --with aiosqlite --with pyserial-asyncio python - <<'PY'
import asyncio, tempfile, os
from callstack.sms.store import SMSStore
from callstack.sms.types import SMS

async def main():
    db = os.path.join(tempfile.gettempdir(), 'callstack-clear-after-close.db')
    try: os.remove(db)
    except FileNotFoundError: pass
    store = SMSStore(db_path=db)
    await store.initialize()
    await store.save(SMS(body='persisted'))
    print('count_after_save:', await store.count())
    await store.close()
    await store.clear()
    print('count_after_clear_closed_memory:', await store.count())
    await store.initialize()
    print('count_after_reinitialize:', await store.count())
    print('bodies_after_reinitialize:', [m.body for m in await store.list()])
    await store.close()
    os.remove(db)

asyncio.run(main())
PY

Actual output:

count_after_save: 1
count_after_clear_closed_memory: 0
count_after_reinitialize: 1
bodies_after_reinitialize: ['persisted']

Expected behavior

For a SQLite-backed store, await store.clear() should mean durable clear, not only in-memory clear. After clear() returns, reopening the same store should not reload rows that were supposedly cleared.

Suggested fix direction

Make clear() durable when db_path is configured even if the connection is currently closed. Possible approaches:

  • Open/init the DB inside clear() long enough to execute DELETE FROM messages, preserving the caller's previous open/closed state; or
  • Record a pending clear tombstone that initialize() applies before loading rows.

The first option is simpler but should avoid surprising callers by leaving a previously closed store open.

Acceptance criteria

  • Regression test: save a row to SQLite, close(), clear(), initialize(), then count() == 0.
  • clear() before first initialize() still behaves correctly.
  • clear() while open still deletes persisted rows.
  • Pending saves made while closed are not resurrected after a clear.

Verification gates

git diff --check
PYTHONPATH=. uv run --no-project --with pytest --with pytest-asyncio --with pytest-aiohttp --with pyserial-asyncio --with aiosqlite pytest tests/test_sms_store.py -q
PYTHONPATH=. uv run --no-project --with pytest --with pytest-asyncio --with pytest-aiohttp --with pyserial-asyncio --with aiosqlite pytest tests/ -q

Duplicate check

Checked open/closed issues for SMSStore clear close initialize SQLite rows, durable store clear reopen pending SQLite, and clear SMSStore SQLite; no matching issue or PR found.

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