Рефакторинг границ capabilities: цепочка загрузка→матч→ревью→раскладка (openspec)
Привёл набор capabilities в OpenSpec к цепочке обработки, чтобы имя capability отвечало одному поведению. Чисто по спекам, код и поведение системы не меняются. Change refactor-capability-boundaries (архивирован): - recognition разделён на recognition (разбор LLM) + metadata-match (сверка с базами) - review выделен из web-ui + мигрирован из docs/specs/review-ux.md - новые capability из docs/specs: file-layout, download-tracking, notifications - identity очищен до инфра-id; приём (инфохэши, дедуп, ядро приёма) — в ingest - уведомление о рассинхроне перенесено из state-reconciliation в notifications - дубль владения путём и безопасного undo оставлен в state-reconciliation Итог: 11 capabilities, openspec validate --strict проходит (+37/−11 требований). Источник истины по мигрированным темам переехал в openspec/specs (шапки в docs). Снят пункт беклога «Пересмотр набора capabilities». Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+118
@@ -0,0 +1,118 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Пред-парс имени релиза
|
||||
|
||||
Перед вызовом LLM система SHALL выполнять дешёвый пред-парс имени торрента
|
||||
(`go-ptn`): извлекать черновые название, год, сезон, серию и качество. Результат
|
||||
пред-парса SHALL использоваться как вспомогательный сигнал в промпте и как
|
||||
сторона проверки согласованности при решении auto/review, но НЕ SHALL считаться
|
||||
итоговым распознаванием.
|
||||
|
||||
#### Scenario: Пред-парс даёт черновые поля
|
||||
|
||||
- **WHEN** на вход распознавания поступает имя релиза `Fargo.S02.2015.WEB-DL.1080p`
|
||||
- **THEN** пред-парс возвращает черновые `title`, `year`, `season`, `quality`
|
||||
- **AND** эти значения передаются в промпт LLM как подсказка
|
||||
|
||||
### Requirement: Разбор сигналов LLM в структурированный план
|
||||
|
||||
Система SHALL передавать LLM недоверенные сигналы (имя торрента, дерево файлов с
|
||||
размерами, текстовый контекст и накопленные подсказки, пред-парс) и получать
|
||||
структурированный план в схеме: `type` (`movie`|`series`), `title`,
|
||||
`original_title`, `year`, `provider_hint`, `files[]` и `confidence`. Каждый
|
||||
элемент `files[]` SHALL нести `src`, `role`
|
||||
(`main`|`episode`|`subtitle`|`extra`|`sample`|`ignore`) и, для сериала,
|
||||
per-file `season`/`episode` (отдельного скалярного `season` быть SHALL NOT — так
|
||||
выражаются мультисезонные паки и спецвыпуски). План SHALL приниматься только
|
||||
если каждый `files[].src` совпадает с реальным файлом торрента.
|
||||
|
||||
#### Scenario: План сериала с per-file нумерацией
|
||||
|
||||
- **GIVEN** сезон-пак из 10 видеофайлов
|
||||
- **WHEN** LLM возвращает план
|
||||
- **THEN** `type` = `series`, а каждый видеофайл несёт свои `season`/`episode`
|
||||
|
||||
#### Scenario: Несуществующий src отклоняется
|
||||
|
||||
- **GIVEN** ответ LLM, где `files[].src` не совпадает ни с одним файлом торрента
|
||||
- **WHEN** план разбирается
|
||||
- **THEN** такой план не принимается как валидный
|
||||
|
||||
### Requirement: Провайдер LLM за абстракцией со структурированным выводом
|
||||
|
||||
Доступ к LLM SHALL быть за интерфейсом с выбором реализации по полю `[llm].type`
|
||||
(первый тип — `openai-compat`). Система SHALL запрашивать JSON-режим
|
||||
(`response_format: {"type":"json_object"}`), срезать ```-ограждения и
|
||||
валидировать ответ в Go против схемы плана. При ошибке разбора система SHALL
|
||||
ретраить до `[llm].max_retries`, передавая модели саму ошибку и схему. Если после
|
||||
ретраев ответ не разобран, задача SHALL уходить в `review` (НЕ в `failed`) с
|
||||
причиной «ответ LLM не разобран».
|
||||
|
||||
#### Scenario: Неразобранный ответ уходит в review
|
||||
|
||||
- **GIVEN** LLM, чей ответ не проходит валидацию схемы после всех ретраев
|
||||
- **WHEN** завершается распознавание
|
||||
- **THEN** задача переходит в `review` с причиной «ответ LLM не разобран»
|
||||
- **AND** задача НЕ переходит в `failed`
|
||||
|
||||
### Requirement: Модель уверенности и решение auto/review
|
||||
|
||||
Система SHALL раскладывать автоматически (без review) только при выполнении
|
||||
ВСЕГО: (1) подтверждённый единичный сильный матч в базе (`metadata-match`) с
|
||||
`provider_id`; (2) структурная валидация без предупреждений (фильм — ровно один
|
||||
основной видеофайл; сериал — число серий бьётся с базой, нумерация S·E
|
||||
консистентна); (3) согласованность пред-парса и LLM по типу/названию/году. Иначе
|
||||
задача SHALL уходить в `review` с явной причиной. Самооценку LLM (`confidence`)
|
||||
система SHALL учитывать лишь как вспомогательный сигнал, НЕ как единственный гейт.
|
||||
|
||||
#### Scenario: Нет матча в базе — всегда review
|
||||
|
||||
- **GIVEN** план без подтверждённого матча в базе (база выключена или матча нет)
|
||||
- **WHEN** принимается решение auto/review
|
||||
- **THEN** задача уходит в `review`, авто-раскладка не делается
|
||||
|
||||
#### Scenario: Матч и чистая валидация — авто
|
||||
|
||||
- **GIVEN** подтверждённый единичный матч, чистая структурная валидация и
|
||||
согласованность сигналов
|
||||
- **WHEN** принимается решение
|
||||
- **THEN** допускается авто-раскладка (при отсутствии `force_review`)
|
||||
|
||||
### Requirement: Роли файлов на краях раздачи
|
||||
|
||||
Система SHALL относить семплы, «экстра» и мусор к роли `ignore` (эвристики размер/
|
||||
имя + LLM), а внешние субтитры (`.srt`, `.ass`, пары VobSub `.idx`+`.sub`) —
|
||||
привязывать к соответствующему видео. Любую неоднозначность нумерации (дыры,
|
||||
дубли, спорные спецвыпуски) система SHALL эскалировать в `review`, а не разрешать
|
||||
молча.
|
||||
|
||||
#### Scenario: Семпл помечается ignore
|
||||
|
||||
- **GIVEN** раздача с файлом `sample.mkv` малого размера
|
||||
- **WHEN** строится план
|
||||
- **THEN** этот файл получает роль `ignore` и в раскладку не попадает
|
||||
|
||||
## REMOVED Requirements
|
||||
|
||||
### Requirement: Сверка с базой по нескольким названиям
|
||||
|
||||
**Reason**: Работа с внешними базами метаданных — отдельное поведение; выделена в
|
||||
capability `metadata-match`.
|
||||
**Migration**: Требование перенесено без изменений в `metadata-match` (см.
|
||||
`specs/metadata-match`).
|
||||
|
||||
### Requirement: Локаль запроса к TMDB
|
||||
|
||||
**Reason**: Относится к работе с метабазой (TMDB), выделенной в `metadata-match`.
|
||||
**Migration**: Требование перенесено без изменений в `metadata-match`.
|
||||
|
||||
### Requirement: Нормализация названий при сравнении
|
||||
|
||||
**Reason**: Нормализация — часть сверки с метабазой, выделенной в `metadata-match`.
|
||||
**Migration**: Требование перенесено без изменений в `metadata-match`.
|
||||
|
||||
### Requirement: Кандидат несёт URL для внешней проверки
|
||||
|
||||
**Reason**: Кандидат — сущность метабазы; контракт кандидата относится к
|
||||
`metadata-match`.
|
||||
**Migration**: Требование перенесено без изменений в `metadata-match`.
|
||||
Reference in New Issue
Block a user