docs: record decisions #16-17 for Task 3/6 deviations from plan's literal code

R-1 (design #16): OpenAiTranscriber.transcribe's redact() call moved out of
the plan's literal post-try/catch position into a single guarded call site,
closing a never-throws gap the plan's own code left open (Task 3 fix loop).

R-2 (design #17): main.ts's settings field needed an override modifier to
satisfy noImplicitOverride against obsidian 1.13.1's new base Plugin.settings
field; the plan's literal code did not compile without it (Task 6).

Refs #5

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKHeGuAYh4W1jNdjMConZa
This commit is contained in:
Andrey Epifancev 2026-09-28 01:11:03 +04:00
parent 4f6f202eda
commit 6f6df01cbd

View File

@ -106,3 +106,5 @@ new OpenAiTranscriber(http: HttpClient, settings: () => OpenAiSettings, random:
13. 2026-09-27 (план) — `redact` дополнительно обрезает причину с первого вхождения boundary тела запроса (`--<boundary>` или сам boundary) или строки `Content-Disposition` (без учёта регистра), затем убирает хвостовые пробелы и двоеточия. *Спека запрещает любую часть тела запроса в причине; единственный реалистичный путь туда — сервис или прокси, который эхом возвращает тело в тексте ошибки. Замена ключа и `Bearer` этого не ловит.* **Если легитимный текст ошибки случайно содержит `Content-Disposition` — деталь обрезается, остаётся `HTTP <код>`; диагностика беднее, данные не теряются.** Название модели или язык в тексте ошибки («model gpt-x not found») частью тела не считаются и не вырезаются. 13. 2026-09-27 (план) — `redact` дополнительно обрезает причину с первого вхождения boundary тела запроса (`--<boundary>` или сам boundary) или строки `Content-Disposition` (без учёта регистра), затем убирает хвостовые пробелы и двоеточия. *Спека запрещает любую часть тела запроса в причине; единственный реалистичный путь туда — сервис или прокси, который эхом возвращает тело в тексте ошибки. Замена ключа и `Bearer` этого не ловит.* **Если легитимный текст ошибки случайно содержит `Content-Disposition` — деталь обрезается, остаётся `HTTP <код>`; диагностика беднее, данные не теряются.** Название модели или язык в тексте ошибки («model gpt-x not found») частью тела не считаются и не вырезаются.
14. 2026-09-27 (план) — `OpenAiTranscriber.transcribe` оборачивает всё тело в `try/catch`; непредвиденное исключение (например, бросил геттер настроек) → `temporary` «внутренняя ошибка: <сообщение>» после `redact`. Исключение HTTP-клиента, в том числе синхронное, по-прежнему `сеть: <сообщение>`. *Спека требует, чтобы провайдер никогда не бросал; очередь такое тоже поймала бы (решение 13 прошлой фичи), но контракт должен держаться сам.* **Ошибка программирования в провайдере маскируется под временную и тратит пять попыток, прежде чем запись уйдёт в `failed`.** 14. 2026-09-27 (план) — `OpenAiTranscriber.transcribe` оборачивает всё тело в `try/catch`; непредвиденное исключение (например, бросил геттер настроек) → `temporary` «внутренняя ошибка: <сообщение>» после `redact`. Исключение HTTP-клиента, в том числе синхронное, по-прежнему `сеть: <сообщение>`. *Спека требует, чтобы провайдер никогда не бросал; очередь такое тоже поймала бы (решение 13 прошлой фичи), но контракт должен держаться сам.* **Ошибка программирования в провайдере маскируется под временную и тратит пять попыток, прежде чем запись уйдёт в `failed`.**
15. 2026-09-27 (план) — Boundary — `----voice-note-` + 24 hex-символа из `random` (`Math.random` в плагине). *Детерминированно в тестах; вероятность совпадения с байтами аудио пренебрежимо мала при 96 битах.* **Совпадение boundary с байтами аудио испортит тело — 400, `permanent`; «Повторить распознавание» сгенерирует новый boundary.** 15. 2026-09-27 (план) — Boundary — `----voice-note-` + 24 hex-символа из `random` (`Math.random` в плагине). *Детерминированно в тестах; вероятность совпадения с байтами аудио пренебрежимо мала при 96 битах.* **Совпадение boundary с байтами аудио испортит тело — 400, `permanent`; «Повторить распознавание» сгенерирует новый boundary.**
16. 2026-09-27 (исполнение, контроллер SDD, R-1) — В `OpenAiTranscriber.transcribe` (Task 3) единственный вызов `redact()` вынесен из `try/catch` в отдельный, тоже защищённый `try/catch` с запасным вариантом — жёстко заданной строкой `'внутренняя ошибка'` без интерполяции — если `redact()` сам бросит исключение. Это отступление от буквального кода плана (Task 3, Step 5): план дословно ставил единственный вызов `redact()` ПОСЛЕ `try/catch`, что ревьюер финального ревью задачи 3 нашёл как реальный (Important) пробел. *Дословный код плана проходил все тесты и typecheck, но противоречил собственному Global Constraint плана «`OpenAiTranscriber.transcribe` никогда не бросает»: гипотетическое будущее изменение `classify()`/`fail()`, из-за которого `message` перестанет быть строкой, привело бы к необработанному исключению за пределами метода — ровно то, что этот метод существует, чтобы предотвратить.* **Цена ошибки: крайне маловероятный, сейчас недостижимый при текущих типах путь; если рулинг был не нужен — цена лишь две защитные строки кода без изменения поведения ни для одного существующего пути.**
17. 2026-09-27 (исполнение, Task 6 implementer, R-2) — В `src/main.ts` поле `settings` класса `VoiceNotePlugin` объявлено с модификатором `override`, которого нет в буквальном коде плана (Task 6, Step 3). *Дословный код плана не проходил typecheck (TS4114): установленный пакет `obsidian` (1.13.1) с версии 1.13.0 объявляет базовое поле `Plugin.settings?: unknown`, а `tsconfig.json` репозитория требует `noImplicitOverride: true` — любое поле подкласса, перекрывающее поле базового класса, обязано нести `override`.* **Цена ошибки: ноль — чисто синтаксический модификатор без изменения поведения; без него код не компилируется, других исходов нет.**