внутренняя модель перестроена вокруг аудиозаписи
- audiorecords вместо transcribe_jobs: приложения (texts, structures, recognitions, record_events, topics) живут своими коллекциями, ссылки на исходник и на приведённую копию перестали переставляться - рубеж называет достигнутое, отказ стал признаком остановки с причиной, а сторожей стало двое: число отказов и время в рубеже - воркеры потеряли специализацию, их число задаётся [pipeline] workers, шаг выбирается по рубежу, а захват отдаёт идентификатор и признак захвата
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
# Критерии приёмки
|
||||
|
||||
Дословно из записи задачи `record-centric-model`. Файл задачи закрытие удалит —
|
||||
критерии обязаны его пережить.
|
||||
|
||||
1. Запись, остановленная на шаге, перезапускается снятием признака и продолжает
|
||||
с того рубежа, где стояла. Оракул — тест: шаг останавливает запись на
|
||||
`normalized`, снятие `halted_at` возвращает её в работу, и следующим идёт
|
||||
отправка на распознавание, а не повторная нормализация.
|
||||
2. У прошедшей конвейер записи ссылки на исходник и на opus ведут на разные
|
||||
существующие копии. Оракул — тест полного прохода: обе ссылки заполнены и обе
|
||||
открываются.
|
||||
3. Число воркеров задаётся конфигом, и поведение от него не зависит. Оракул —
|
||||
прогон теста конвейера при `N=1` и `N=4`: запись доходит до `done` в обоих;
|
||||
при `N=0` она остаётся в `uploaded` и не теряется.
|
||||
4. Структура реплик строится из сохранённого ответа провайдера без обращения к
|
||||
нему. Оракул — тест на сохранённом вложении: структура собрана, клиент
|
||||
SpeechKit не позван ни разу.
|
||||
5. Запись, застрявшая в рубеже дольше предела, останавливается, а откладывание
|
||||
опроса предела не сдвигает. Оракул — тест на подставных часах: сотня
|
||||
откладываний подряд не двигает `state_entered_at` и не обнуляет отсчёт, а по
|
||||
истечении предела запись получает признак остановки с причиной «застряла».
|
||||
|
||||
## Приёмочные критерии из ревью дизайна
|
||||
|
||||
Рубрика прохода `review-rubric`, пункты, не покрытые критериями выше. Приёмка
|
||||
судится по одному списку.
|
||||
|
||||
6. Держатель захвата отличим **значением**, а не занятостью записи. Оракул —
|
||||
тест: шаг A держит запись, признак остановки снимает человек, запись
|
||||
захватывает шаг B; запись результата шагом A не проходит, и отправителю от
|
||||
него ничего не уходит.
|
||||
7. Всякий способ вывести запись из работы сообщает отправителю. Оракул — тест по
|
||||
перечню причин остановки: у каждой отправитель получает сообщение.
|
||||
8. Повтор шага не создаёт второго приложения. Оракул — тест: шаг завершения
|
||||
оборван после записи текста и повторён с прежнего рубежа; строка расшифровки
|
||||
у записи одна.
|
||||
9. Штатная остановка сервиса не тратит отказ. Оракул — тест: шаг прерван
|
||||
отменой контекста, число отказов записи прежнее, признака остановки нет.
|
||||
10. Содержимое записи закрыто во всех коллекциях, где лежит. Оракул — тест:
|
||||
ссылка на сохранённый ответ провайдера без сессии отдаёт отказ, с чужой
|
||||
сессией — тоже.
|
||||
11. Отказ виден с разрезом по шагу. Оракул — тест: отказ шага приведения растит
|
||||
счётчик с меткой своего рубежа.
|
||||
|
||||
## 1. Сущности и рубежи
|
||||
|
||||
- [x] 1.1 Завести `entity.AudioRecord` с рубежом, временем входа в рубеж,
|
||||
признаком остановки и её причиной, полями очереди и ссылками на приложения
|
||||
- [x] 1.2 Дескриптор рубежа одним объявлением: имя, шаг, чья работа, срок захвата,
|
||||
предел времени, берётся ли в работу. Таблица выбора шага, список отбора
|
||||
захвата и пределы **выводятся** из него, а не перечисляются порознь
|
||||
- [x] 1.3 Развести `MoveToState` и `Postpone`: первый двигает рубеж и время входа
|
||||
в него, второй ставит паузу и снимает захват, рубежа не трогая
|
||||
- [x] 1.4 Заменить `Fail` и `Die` на `Halt(причина, текст)` и `Resume()`;
|
||||
`Resume` сбрасывает отказы, паузу **и время входа в рубеж**, рубеж сохраняет
|
||||
- [x] 1.5 Завести сущности приложения: файл, текст с видом, структура реплик с
|
||||
версией, тема, событие журнала, попытка распознавания
|
||||
- [x] 1.6 Тест: `Postpone` не двигает время входа в рубеж и не сбрасывает отсчёт
|
||||
- [x] 1.7 Тест: `Halt` сохраняет рубеж, `Resume` возвращает на него же и заново
|
||||
ставит время входа **(критерий 1)**
|
||||
|
||||
## 2. Шаг схемы
|
||||
|
||||
- [x] 2.1 Новым файлом шага завести коллекции `audio_records`, `texts`,
|
||||
`structures`, `recognitions`, `record_events`, `topics`
|
||||
- [x] 2.2 Дописать `files` полями формата и длительности
|
||||
- [x] 2.3 Уникальность: тема по паре «владелец и название», текст по паре
|
||||
«запись и вид» (`texts.kind`), структура по паре «запись и версия»
|
||||
- [x] 2.4 Индексы под выборку захвата: рубеж, пауза, срок захвата, признак
|
||||
остановки
|
||||
- [x] 2.5 Правила доступа новых коллекций: просмотр только владельцем связанной
|
||||
записи; поле вложения в `recognitions` помечено защищённым
|
||||
- [x] 2.6 Тот же шаг удаляет прежнюю коллекцию `transcribe_jobs`: данных под ней
|
||||
нет, а пустая коллекция висела бы в панели вторым домом для того же понятия
|
||||
- [x] 2.7 Тест шага: на чистом каталоге поднимаются все коллекции новой модели, и
|
||||
принятая следом запись доходит до конечного рубежа
|
||||
- [x] 2.8 Тест: ссылка на сохранённый ответ без сессии и с чужой сессией даёт
|
||||
отказ **(критерий 10)**
|
||||
- [x] 2.9 Обновить `docs/database.md` тем же изменением: гейт сверяет шаг схемы
|
||||
с правкой этого документа
|
||||
|
||||
## 3. Контракты и репозитории
|
||||
|
||||
- [x] 3.1 `AudioRecognizer` отдаёт доменный результат — реплики со временем,
|
||||
плоский текст, байты на хранение — вместо строки; заливка и отправка
|
||||
разделены
|
||||
- [x] 3.2 Контракты репозиториев: запись, файлы, тексты, структура, попытки
|
||||
распознавания, журнал событий, темы
|
||||
- [x] 3.3 Захват возвращает **идентификатор записи и признак этого захвата**;
|
||||
признак уникален для каждого захвата; `acquireColumns` и `acquiredRow`
|
||||
уходят, срок протухания пишется числом при захвате
|
||||
- [x] 3.4 Запрос захвата: отбор по рубежам из дескриптора, паузе, сроку захвата и
|
||||
отсутствию признака остановки, порядок по времени заведения и ключу
|
||||
- [x] 3.5 Запись результата условна по признаку **этого** захвата, а не по
|
||||
занятости; владелец, заголовок, краткое описание и темы шагом не
|
||||
переписываются
|
||||
- [x] 3.6 Перенацелить сканеры `internal/archrules` с колонок захвата на перечень
|
||||
рубежей: дескриптор против списка отбора против значений шага схемы
|
||||
- [x] 3.7 Тест: захват отдаёт идентификатор и признак, троим одновременным
|
||||
достаётся одному
|
||||
- [x] 3.8 Тест: остановленная запись захвату не выдаётся
|
||||
- [x] 3.9 Тест: шаг A теряет захват после снятия остановки человеком и записи не
|
||||
проводит **(критерий 6)**
|
||||
|
||||
## 4. Адаптер распознавания
|
||||
|
||||
- [x] 4.1 Разбор потока `GetRecognition` в реплики со временем внутри адаптера
|
||||
- [x] 4.2 Раздельные заливка в Object Storage и отправка операции; строка попытки
|
||||
заводится до обращения к провайдеру
|
||||
- [x] 4.3 Заливка проверяет объект нужного размера и не повторяется; отправка не
|
||||
повторяется при заведённом идентификаторе операции
|
||||
- [x] 4.4 Адаптер отдаёт сырые байты ответа на хранение вложением
|
||||
- [x] 4.5 Тест: структура собрана из сохранённого вложения, клиент SpeechKit не
|
||||
позван ни разу **(критерий 4)**
|
||||
- [x] 4.6 Тест: объект на месте — заливка не повторяется; идентификатор операции
|
||||
на месте — отправка не повторяется
|
||||
|
||||
## 5. Конвейер
|
||||
|
||||
- [x] 5.1 Выбор шага по рубежу из дескриптора; шаги перестают быть привязаны к
|
||||
воркеру
|
||||
- [x] 5.2 Шаг приведения: две отдельные ссылки на файлы вместо одной
|
||||
переставляемой
|
||||
- [x] 5.3 Шаг отправки: строка попытки распознавания, идентификатор операции и
|
||||
ключ объекта уезжают туда
|
||||
- [x] 5.4 Шаг опроса зовёт `Postpone`, а не переход в то же состояние
|
||||
- [x] 5.5 Шаг завершения: текст строкой `texts`, структура строкой `structures`,
|
||||
сырой ответ вложением; повтор не заводит второго комплекта
|
||||
- [x] 5.6 Предел времени в рубеже из дескриптора: остановка с причиной «застряла»,
|
||||
идентификатор операции при этом сохраняется
|
||||
- [x] 5.7 Единое место ответа отправителю на всякую остановку, независимо от
|
||||
причины
|
||||
- [x] 5.8 Отмена по остановке сервиса возвращает число отказов назад и приговора
|
||||
не выносит
|
||||
- [x] 5.9 Журнал событий пишется на смену рубежа, на остановку и на снятие
|
||||
остановки; на откладывание — нет; колонка текста отказа зовётся
|
||||
`outcome_text`, чтобы `error_text` осталось именем одной колонки
|
||||
- [x] 5.10 Тест полного прохода: обе ссылки на файлы заполнены и обе открываются
|
||||
**(критерий 2)**
|
||||
- [x] 5.11 Тест: остановка на `normalized`, снятие признака, следующим идёт
|
||||
отправка **(критерий 1)**
|
||||
- [x] 5.12 Тест на подставных часах: сотня откладываний не двигает
|
||||
`state_entered_at`, по истечении предела запись останавливается с причиной
|
||||
«застряла» **(критерий 5)**
|
||||
- [x] 5.13 Тест по перечню причин остановки: у каждой отправитель получает
|
||||
сообщение **(критерий 7)**
|
||||
- [x] 5.14 Тест: повтор шага завершения не заводит второй расшифровки
|
||||
**(критерий 8)**
|
||||
- [x] 5.15 Тест: отмена контекста не тратит отказ **(критерий 9)**
|
||||
|
||||
## 6. Воркеры, метрики и настройки
|
||||
|
||||
- [x] 6.1 Пул одинаковых воркеров вместо трёх именованных в `main.go`
|
||||
- [x] 6.2 Метка счётчика `transcriber_worker_job_count` — рубеж, а не имя потока
|
||||
- [x] 6.3 Число воркеров и два предела времени — в `internal/config` и
|
||||
`config.example.toml` с умолчаниями; имена ключей названы в дизайне
|
||||
- [x] 6.4 `N = 0` поднимает сервис без движения записей
|
||||
- [x] 6.5 Тест конвейера при `N=1` и `N=4` — запись доходит до `done`; при `N=0`
|
||||
остаётся в `uploaded` **(критерий 3)**
|
||||
- [x] 6.6 Тест: отказ шага приведения растит счётчик с меткой своего рубежа
|
||||
**(критерий 11)**
|
||||
|
||||
## 7. Поверхность и панель
|
||||
|
||||
- [x] 7.1 Ответ приёма отдаёт рубеж `uploaded`; ответ опроса — рубеж из нового
|
||||
перечня плюс поле `halted`
|
||||
- [x] 7.2 Текст расшифровки в ответе опроса читается из `texts` по **виду**
|
||||
«сырая расшифровка», а не по последней записи
|
||||
- [x] 7.3 Правила панели: снятие признака остановки и правка рубежа очищают
|
||||
захват, срок, паузу и отказы и заново ставят время входа в рубеж
|
||||
- [x] 7.4 Запрет удаления учётной записи считает `audio_records` и `files`
|
||||
- [x] 7.5 Тесты транспорта под новые значения `status` и поле `halted`
|
||||
|
||||
## 8. Документы и гейт
|
||||
|
||||
- [x] 8.1 Инвариант `CLAUDE.md` о колонках очереди: перечень колонок съёживается,
|
||||
предмет правила переезжает на перечень рубежей
|
||||
- [x] 8.2 Инвариант `CLAUDE.md` о держателе захвата: держатель отличим значением
|
||||
признака захвата, а не занятостью записи
|
||||
- [x] 8.3 `docs/architecture.md`: компоненты, цепочка рубежей, единые точки,
|
||||
таблица внешних зависимостей, раздел «Эксплуатация» — чем владелец теперь
|
||||
замечает отказ
|
||||
- [x] 8.4 `docs/database.md`: коллекции, представление данных, настройки с
|
||||
числовым значением (число воркеров, два предела времени, сроки захвата)
|
||||
- [x] 8.5 `docs/review.md`: снять ложноположительное «гонка захвата по построению
|
||||
невозможна» — построение снято пулом одинаковых воркеров
|
||||
- [x] 8.6 Разделы `Purpose` спек `pipeline` и `storage` при архивации: цепочка
|
||||
рубежей перестала быть «сознательно не описанной», а строка про непереносимые
|
||||
прежние данные — верной
|
||||
- [x] 8.7 `task gate` зелёный целиком
|
||||
- [x] 8.8 Поведенческая проверка живым запуском: подъём с `telegram.enabled =
|
||||
false` и подставным распознавателем, запись доходит до `done`
|
||||
Reference in New Issue
Block a user