# 🐞 Убирать записанный файл, когда приём отказал на середине

- **Тип:** fix
- **Категория:** Очередь — Осиротевший файл — тот же отказ на середине, и чинится тем же местом приёма.
- **Зачем:** Отказ чтения метаданных и отказ записи на диск оставляют файл в каталоге хранения без задачи и без учёта: сопоставить его не с чем, удалять приходится руками.
- **Теги:** review-2026-08-11

Приём пишет файл на диск, потом спрашивает у источника метаданных длительность,
потом заводит запись в учёте и задачу. Уборка при отказе есть **только на
последнем шаге**: отказ `fileRepo.Create` зовёт `os.Remove`, а отказы записи на
диск и чтения метаданных возвращают ошибку, оставляя файл лежать.

Сопоставить такой файл не с чем: записи в `files` под него нет, задачи нет,
имя — случайный идентификатор. На сервере это `data/files`, где лежат голосовые
сообщения живых людей, а проект по паспорту «не хранит записи как архив».

Найдено проходом `review-specs` ревью дизайна change
`2026-08-11-fix-http-handler-tests`; отчёт триажа —
`openspec/changes/archive/2026-08-11-fix-http-handler-tests/review/triage.md`.
Вынесено сюда решением человека на контрольной точке.

## Воспроизведение

Асимметрия видна в самом коде:

```
sed -n '110,160p' internal/service/transcribe.go
```

Ветка `fileRepo.Create` (строки 152–157) содержит `os.Remove(storageFilePath)` с
комментарием «Удаляем файл если не удалось создать запись в БД». Ветки
`io.Copy`, `dst.Close` и `metaviewer.GetInfo` (строки 116–133) возвращают ошибку
без уборки, хотя файл к этому моменту уже создан.

Прогоном: `TestCreateTranscribeJob_MetaViewerFailure` в
`internal/controller/http/transcribe_test.go` доводит приём до этой ветки —
после него в каталоге хранения лежит файл, а `countJobs` равен нулю.

## Затрагивает

- `internal/service/transcribe.go`, функция `createTranscribeJob` — три ветки
  отказа между созданием файла и заведением записи в учёте;
- `internal/controller/http/transcribe_test.go` — случай отказа метаданных
  получает проверку на отсутствие файла;
- раскладка `data/files` не меняется: имена и формат прежние.

## Критерии приёмки

- Отказ чтения метаданных не оставляет файла в каталоге хранения. Оракул — тест
  на подставном источнике, возвращающем ошибку: каталог пуст после запроса.
- Отказ записи на диск на середине не оставляет частично записанного файла.
  Оракул — тест с источником данных, обрывающимся на середине чтения.
- Уборка сама не заслоняет исходную ошибку: отправитель по-прежнему получает
  отказ, а причина уезжает в журнал. Оракул — тот же тест, код ответа и запись в
  перехваченном журнале.

## Рамки

Уборка файлов, осиротевших **до** этой правки, сюда не входит: их надо найти
сверкой каталога с учётом, и это отдельная работа с боевыми данными.

