Перейти к содержанию

ADR 0045: Persistence истории чата — append-only события + проекции

Статус: Accepted · Implemented
Дата: 2026-04-14

Связанные ADR

ADR Роль
0031 модель пакетов уточнений
0044 модель первична, UI вторичен
0043 транспорт не равен хранению истории

Контекст

Чат в CIDE перестаёт быть «одноразовым полем» и становится рабочей поверхностью с:

  • сообщениями и стримингом;
  • пакетами уточнений (ClarificationBatch);
  • потенциальными ветками/тредами;
  • связями с инструментами и артефактами.

Для такой формы нужна persistence, которая:

  1. не привязана к конкретному UI;
  2. переживает эволюцию схемы;
  3. восстанавливается детерминированно после рестарта.

Решение (направление)

  1. Канон хранения — append-only event log в NDJSON (*.events.ndjson), одно событие на строку.
  2. Метаданные сессии — отдельный *.meta.json (id, created/updated, title, версия).
  3. UI работает через проекции (in-memory сейчас; индекс/поиск можно добавить позже), а не напрямую через «файлы сообщений».
  4. Payload у события хранится как JSON-объект (строка JSON в модели) с schema_version, чтобы расширять поля без миграции всей истории.
  5. Вложения и тяжёлые данные — отдельными файлами; в событии хранить только ссылку/идентификатор.

Формат v1 (минимум)

Каталог: workspace/.cascade-ide/chat-sessions/

  • session-<id>.events.ndjson
  • session-<id>.meta.json

Типы событий v1:

  • message_added
  • message_stream_delta
  • message_completed
  • clarification_batch_opened
  • clarification_answer_submitted
  • message_edited — компенсирующее событие: новый текст для существующего message_id в payload (message_added / message_completed не переписываются).

Полезная нагрузка message_added и message_completed v1 включает стабильный message_id (строка без дефисов), role, content.

Экспорт для агента: читаемый Markdown из текущей проекции (команда chat_export_readable, опционально запись в chat-sessions/exports/).

Почему не «сразу SQLite как источник правды»

  • На ранней фазе продукта event log проще отлаживать, diff-ить и мигрировать.
  • Проекции можно менять без переписывания канона.
  • SQLite-индекс можно добавить как ускоритель, не ломая формат истины.

Последствия

  • Появляется единый слой восстановления истории для человека и агента.
  • Появляется дисциплина версионирования payload.
  • Нужен retention/архив (политика размера и возраста) отдельным шагом.

Открытые вопросы

  • Политика очистки/архива (N дней, M МБ, ручной pin).
  • Политика редактирования старых сообщений (компенсирующее событие vs hard rewrite).
  • Минимальный набор редактирования секретов (redaction event).