layout: непомещающееся целевое имя уводит задачу в review вместо failed
- предел длины компонента (255 байт) проверяется в BuildLinks до первой операции с ФС: ни каталога, ни ссылки при отказе не создаётся - причина пустого предпросмотра считается на показе (ReviewData.PreviewError) и печатается в панели действий и в карточке Telegram: у задачи без записанной причины взять её больше неоткуда
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Непомещающееся целевое имя уходит в review
|
||||
|
||||
Система SHALL отклонять раскладку целиком, если хотя бы один компонент целевого
|
||||
пути (папка тайтла, папка сезона, имя файла с расширением и суффиксами) длиннее
|
||||
предела, и SHALL переводить задачу в `review` с доменной причиной, а не в
|
||||
`failed`. Проверка SHALL выполняться до первой операции с файловой системой: ни
|
||||
каталога, ни ссылки при отказе не создаётся. Причина SHALL нести собственный код,
|
||||
отличный и от кода коллизии, и от общего кода отказа построения плана, а
|
||||
человекочитаемый текст SHALL называть непомещающийся компонент и НЕ SHALL
|
||||
содержать текста системной ошибки.
|
||||
|
||||
Предел SHALL меряться в **байтах** UTF-8-представления имени, а не в символах:
|
||||
кириллическое название упирается вдвое раньше латинского той же длины в знаках.
|
||||
Величина — **255 байт** (`NAME_MAX` у ext4/xfs/btrfs); она фиксирована и у ядра не
|
||||
выясняется, потому что раскладка обязана отказать до обращения к диску. На
|
||||
файловой системе с меньшим пределом остаётся сегодняшний исход — отказ ядра и
|
||||
`failed`; это осознанный остаток, а не пробел.
|
||||
|
||||
Проверка длины SHALL выполняться **после** проверки нахождения пути под корнем
|
||||
библиотеки: путь, вышедший за песочницу, SHALL отклоняться как выход за
|
||||
библиотеку, иначе находка безопасности спряталась бы за косметической причиной.
|
||||
|
||||
Самостоятельно обрезать или переименовывать название система НЕ SHALL — это
|
||||
решение человека.
|
||||
|
||||
#### Scenario: Слишком длинное имя файла уходит в review
|
||||
|
||||
- **GIVEN** распознавание даёт название, из которого имя целевого файла
|
||||
складывается длиннее 255 байт
|
||||
- **WHEN** выполняется раскладка
|
||||
- **THEN** задача переходит в `review` с причиной «имя не помещается», ни один
|
||||
каталог и ни одна ссылка не созданы, а текст системной ошибки человеку не
|
||||
показан
|
||||
|
||||
#### Scenario: Предел меряется в байтах
|
||||
|
||||
- **GIVEN** два названия одинаковой длины в знаках — латинское и кириллическое
|
||||
- **WHEN** строятся целевые пути
|
||||
- **THEN** кириллическое отклоняется вдвое раньше латинского, а граница
|
||||
проходит между 255 и 256 байтами
|
||||
|
||||
#### Scenario: Слишком длинное имя папки тайтла уходит в review
|
||||
|
||||
- **GIVEN** название укладывается в имя файла, но папка тайтла с годом и
|
||||
provider-тегом длиннее предела
|
||||
- **WHEN** выполняется раскладка
|
||||
- **THEN** задача переходит в `review` с той же причиной, каталог тайтла не
|
||||
создан
|
||||
|
||||
#### Scenario: Выход за библиотеку важнее длины
|
||||
|
||||
- **GIVEN** целевой путь одновременно выходит за корень библиотеки и длиннее
|
||||
предела
|
||||
- **WHEN** строятся целевые пути
|
||||
- **THEN** отказ называет выход за библиотеку, а не длину имени
|
||||
|
||||
#### Scenario: Подсказка человека чинит случай
|
||||
|
||||
- **GIVEN** задача в `review` с причиной «имя не помещается»
|
||||
- **WHEN** человек задаёт название короче и применяет раскладку заново
|
||||
- **THEN** раскладка проходит штатно и задача переходит в `done`
|
||||
|
||||
#### Scenario: Унаследованная база в предел помещается всегда
|
||||
|
||||
- **GIVEN** база имени унаследована от живой папки-якоря по правилу сходимости
|
||||
- **WHEN** строятся имена файлов внутри этой папки
|
||||
- **THEN** они помещаются в предел, потому что папка-якорь лежит на диске и уже
|
||||
не длиннее предела, а хвост имени файла не длиннее хвоста имени папки
|
||||
@@ -0,0 +1,63 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Панель действий при пустом предпросмотре называет причину
|
||||
|
||||
Панель действий SHALL называть причину, когда предпросмотр раскладки пуст, а не
|
||||
печатать общее «Подтверди источник, чтобы получить превью раскладки». Первой
|
||||
SHALL идти причина, посчитанная **на показе** — отказ построения этого
|
||||
предпросмотра: она относится к текущему эффективному плану, тогда как записанная
|
||||
при последнем переходе после смены источника устаревает, а у задачи, пришедшей в
|
||||
`review` без записанной причины, её нет вовсе. Записанная причина SHALL
|
||||
использоваться, когда посчитанной нет. Общий текст SHALL оставаться только там,
|
||||
где нет ни той, ни другой — источник действительно ещё не подтверждён.
|
||||
|
||||
Построение предпросмотра НЕ SHALL двигать состояние задачи: причина считается на
|
||||
чтении и наружу отдаётся значением, а не записью.
|
||||
|
||||
Те же две причины в том же порядке SHALL показываться и в карточке Telegram,
|
||||
когда плана в ней нет: обе поверхности ревью объясняют отсутствие команды
|
||||
«Применить» одинаково. Команда, упершаяся в непомещающееся имя, SHALL отвечать
|
||||
конфликтом, а не сбоем сервера.
|
||||
|
||||
Требование не трогает доступность команды «Применить»: она по-прежнему следует
|
||||
наличию предпросмотра. Речь о том, что человеку говорят, когда предпросмотра нет:
|
||||
пустой предпросмотр наступает и от коллизии путей, и от непомещающегося имени, и
|
||||
от невалидного плана, а текст сегодня во всех случаях один и в трёх из четырёх
|
||||
неверен.
|
||||
|
||||
#### Scenario: Непомещающееся имя названо в панели действий
|
||||
|
||||
- **GIVEN** загрузка в `review` с причиной «имя не помещается», источник
|
||||
подтверждён, предпросмотр пуст
|
||||
- **WHEN** человек открывает экран ревью
|
||||
- **THEN** панель действий печатает причину отказа, а не предложение подтвердить
|
||||
источник, и команда «Применить» недоступна
|
||||
|
||||
#### Scenario: Причина не записана в состоянии — считается на показе
|
||||
|
||||
- **GIVEN** загрузка пришла в `review` без записанной причины (нет матча), а её
|
||||
название не помещается в имя файла
|
||||
- **WHEN** человек открывает экран ревью
|
||||
- **THEN** панель действий называет длину имени, хотя в состоянии причины нет, и
|
||||
состояние при этом не меняется
|
||||
|
||||
#### Scenario: После смены источника показывается свежая причина
|
||||
|
||||
- **GIVEN** загрузка в `review` с записанной причиной «имя не помещается», и
|
||||
человек выбрал другой источник
|
||||
- **WHEN** экран перестраивается
|
||||
- **THEN** показывается причина, посчитанная для нового плана, а не записанная
|
||||
при прошлом переходе
|
||||
|
||||
#### Scenario: Карточка Telegram называет ту же причину
|
||||
|
||||
- **GIVEN** загрузка в `review`, плана в карточке нет
|
||||
- **WHEN** карточка отправляется или обновляется
|
||||
- **THEN** в ней есть строка с причиной, по которой план не построился
|
||||
|
||||
#### Scenario: Источник не подтверждён — текст прежний
|
||||
|
||||
- **GIVEN** загрузка в `review` без записанной причины, без посчитанной и без
|
||||
предпросмотра
|
||||
- **WHEN** человек открывает экран ревью
|
||||
- **THEN** панель действий печатает общее предложение подтвердить источник
|
||||
Reference in New Issue
Block a user