docs: implementation plan for core recording lifecycle; planning decisions

Refs #1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andrey Epifancev 2026-09-27 15:14:25 +04:00
parent 1cd72201e2
commit cb9a691931
2 changed files with 2540 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@ -113,3 +113,15 @@ interface Env { now(): Date; random(): number; deviceId: string; }
12. 2026-09-27 — `NoteStore.list()` отдаёт frontmatter-объект, а не текст заметки; core перепроверяет состояние внутри `process` по актуальному содержимому и пропускает запись, если она уже не в ожидаемом состоянии или не своего устройства. *Следствие правки 2: без чтения файлов текста нет, а `metadataCache` обновляется асинхронно.* **Без перепроверки — повторная отправка только что отправленной записи.**
13. 2026-09-27 — Исключение из `RecordingStore.read` или `Transcriber.transcribe` трактуется как временная ошибка со своим сообщением. *Классифицирует провайдер, но непредвиденный throw не должен ронять очередь.* **Если исключение на деле постоянное (например, аудиофайл удалён) — 5 бесполезных попыток за ~42 минуты, затем `failed`.**
14. 2026-09-27 — Причина «нет речи» хранится кодом `voice-error: no-speech`, а не текстом. *Рендер должен отличать её от обычной ошибки без разбора сообщений.* **Нулевая: значение внутреннее.**
15. 2026-09-27 (план) — Все строковые значения `voice-*` пишутся в двойных кавычках через `JSON.stringify`; числа (`voice-attempts`) и `true` — без кавычек. *JSON-строка — валидная YAML-строка: переводы строк, кавычки и двоеточия из сообщений провайдера экранируются в одну строку; id вида `20260927-...` не рискует распарситься YAML как число/дата.* **Кавычки видны в режиме исходника — косметика.**
16. 2026-09-27 (план) — Из `retry` запись переходит сразу в `sent`; промежуточное `saved` (на схеме «срок наступил → saved») не записывается. *Лишняя запись в файл без наблюдаемой разницы.* **Нулевая: `saved` и «`retry` с наступившим сроком» обрабатываются одинаково.**
17. 2026-09-27 (план) — «Повторить распознавание» переназначает запись текущему устройству. *Иначе `failed`-запись с телефона, повторённая на маке, вернулась бы в `saved` с устройством телефона, и мак её не отправил бы — команда выглядела бы сломанной.* **Если телефон одновременно тоже повторит ту же запись — один лишний запрос; текст не портится.**
18. 2026-09-27 (план) — Тесты идут с `TZ=Europe/Moscow` (задаётся в `vitest.config.ts`). *Имена заметок и строки статуса зависят от локального времени; UTC+3 без летнего времени даёт детерминизм и заметно отличается от UTC.* **Если CI окажется на Windows, где `TZ` игнорируется — тесты с локальным временем упадут; сейчас CI нет.**
19. 2026-09-27 (план) — id плагина `voice-note`; `npm run dev` после каждой сборки копирует `main.js` и `manifest.json` в `<тестовый vault>/.obsidian/plugins/voice-note/`, если vault есть на машине. *CLAUDE.md: «dev — сборка в тестовый vault».* **Смена id позже — переустановка плагина в vault; данные core живут во frontmatter, не теряются.**
20. 2026-09-27 (план) — Если маркеры области стёрты, а запись ещё не `inserted` (retry/failed), плагин обновляет только frontmatter, строку статуса в тело не дописывает; транскрипт при успехе дописывается в конец. *Не мусорить в заметке, которую пользователь сознательно переделал.* **Пользователь не видит в теле статус ожидания/ошибки такой заметки — он остаётся в Properties.**
21. 2026-09-27 (план) — Причина ошибки в callout сжимается в одну строку (пробельные символы → один пробел). *Многострочное сообщение ломает callout.* **Потеря переносов в тексте ошибки — косметика; полное сообщение остаётся во frontmatter.**
22. 2026-09-27 (план) — Готовые к отправке записи обрабатываются по возрастанию `voice-id` (старые первыми). *Предсказуемо; id по UTC упорядочен во времени.* **Нулевая.**
23. 2026-09-27 (план) — Нераспознаваемое `voice-next-attempt` — запись готова к отправке; отсутствующее или нераспознаваемое `voice-sent-at` у `sent` — отправка считается зависшей. *Ручная правка frontmatter не должна вешать запись навсегда.* **Один досрочный запрос.**
24. 2026-09-27 (план) — `QueueRunner.dispose()` отменяет `AbortSignal`, переданный провайдеру; прерванный запрос обрабатывается как любое исключение — временная ошибка. *Контракт `Transcriber` принимает signal; выгрузка плагина должна обрывать загрузку.* **Выгрузка посреди запроса тратит одну попытку из пяти.**
25. 2026-09-27 (план) — Golden-кейсы находятся по файлам `tests/fixtures/*.note.md`; аудио ищется как `.m4a`, `.webm`, `.ogg` с mime `audio/mp4`, `audio/webm`, `audio/ogg`. *Добавить кейс — положить три файла, без правки теста.* **Нулевая.**
26. 2026-09-27 (план) — Модели исполнителей: haiku — `naming`, `schedule` (механика по готовому коду); opus — `QueueRunner.tick` (асинхронность, гонки); sonnet — остальное. Режим — subagent-driven-development (10 задач > 5). *Правила Model Selection SDD и глобальный CLAUDE.md.* **Слабая модель на механической задаче — лишний круг ревью, не дефект в ветке.**