# Thread Memory Storage Migration: `memory_md` -> `memory_json` ## Summary Per-thread memory now uses `thread_memory.memory_json` as the primary storage format. - New writes persist structured JSON into `memory_json`. - Reads prefer `memory_json`. - Runtime no longer depends on `memory_md`. ## Why `memory_md` stores structured state inside Markdown fenced blocks. This is readable for humans, but costly for: - querying and analytics - schema evolution - migration reliability `memory_json` keeps the same logical payload while making storage machine-friendly. ## Runtime behavior - Read path uses `memory_json` only. - Write path uses `memory_json` only. ## Auto migration behavior - SQLite: on startup, adds `memory_json` column when missing. - MySQL: on startup, adds `memory_json` column when missing. No destructive migration is required for existing data. ## One-shot operational backfill (legacy command) For faster cleanup in production, run: ```bash cd backend UV_CACHE_DIR=/tmp/uv-cache uv run python scripts/backfill_thread_memory_json.py --dry-run UV_CACHE_DIR=/tmp/uv-cache uv run python scripts/backfill_thread_memory_json.py ``` Current codebase keeps this command for compatibility. In fully migrated environments it returns zero legacy rows. ## Final cleanup: drop `memory_md` column After confirming all environments are migrated, run: ```bash cd backend UV_CACHE_DIR=/tmp/uv-cache uv run python scripts/drop_thread_memory_md_column.py --dry-run UV_CACHE_DIR=/tmp/uv-cache uv run python scripts/drop_thread_memory_md_column.py ``` Notes: - SQLite migration rebuilds `thread_memory` table and preserves data. - MySQL migration runs `ALTER TABLE ... DROP COLUMN memory_md`. ## Follow-up (optional) After all active environments have fully migrated and no legacy rows remain: 1. backfill any remaining rows that still rely on `memory_md` 2. remove `memory_md` column from schema 3. remove Markdown parsing fallback code