docs: ссылки на упразднённый роадмап переадресованы, у восьми фактов назван дом
- ссылки на tasks/ROADMAP.md переведены на BACKLOG.md и на openspec/specs, упоминания целей — на задачи, которые эту работу делают; - судьи документации нашли восемь расхождений: Purpose спеки pipeline объявлял неописанным то, что уже нормирован пятью требованиями, вид времени в конвенции спорил со схемой, а квоты, шесть часов и отказ от Web Push жили сразу в двух документах без ссылки друг на друга; - два числа получили провенанс: 259 200 запросов в сутки и потолок в шесть часов теперь ведут к записке разведки, а не читаются как замер.
This commit is contained in:
+10
-6
@@ -5,7 +5,7 @@
|
|||||||
помечены маркером долга и переезжают туда первой же задачей, которая их трогает.
|
помечены маркером долга и переезжают туда первой же задачей, которая их трогает.
|
||||||
|
|
||||||
Документ описывает **сегодняшнее** устройство. Куда проект идёт — в
|
Документ описывает **сегодняшнее** устройство. Куда проект идёт — в
|
||||||
[passport.md](passport.md) и в [tasks/ROADMAP.md](../tasks/ROADMAP.md); что из
|
[passport.md](passport.md) и в [tasks/BACKLOG.md](../tasks/BACKLOG.md); что из
|
||||||
этого ещё не решено — в разделе «Открытые вопросы».
|
этого ещё не решено — в разделе «Открытые вопросы».
|
||||||
|
|
||||||
Заведены пять capability. Четыре первые нормируют **поведение сервиса** для его
|
Заведены пять capability. Четыре первые нормируют **поведение сервиса** для его
|
||||||
@@ -81,7 +81,7 @@
|
|||||||
| Отправитель Telegram | `internal/adapter/telegram` | Отправка текста, деление длинного по словам |
|
| Отправитель Telegram | `internal/adapter/telegram` | Отправка текста, деление длинного по словам |
|
||||||
| Репозитории | `internal/adapter/repo/pocketbase` | Задачи и файлы коллекциями хранилища; захват — сырым запросом |
|
| Репозитории | `internal/adapter/repo/pocketbase` | Задачи и файлы коллекциями хранилища; захват — сырым запросом |
|
||||||
| Шаги схемы | `internal/adapter/repo/pocketbase/migrations` | Файл на шаг, имя файла — имя шага; там же имена коллекций |
|
| Шаги схемы | `internal/adapter/repo/pocketbase/migrations` | Файл на шаг, имя файла — имя шага; там же имена коллекций |
|
||||||
| Панель владельца | `internal/adapter/repo/pocketbase`, `panel.go` | Правка задачи в панели проходит те же правила перехода, что и правка из кода |
|
| Панель владельца | `internal/adapter/repo/pocketbase`, `panel.go` | Панель хранилища; правила правки задачи нормирует [storage](../openspec/specs/storage/spec.md), «Владелец видит записи в панели» |
|
||||||
|
|
||||||
<!-- канон: поведение → openspec/specs/pipeline; ещё НЕ переехало: цепочка переходов состояний -->
|
<!-- канон: поведение → openspec/specs/pipeline; ещё НЕ переехало: цепочка переходов состояний -->
|
||||||
|
|
||||||
@@ -192,8 +192,11 @@
|
|||||||
текст расшифровки начинает уходить на сторону — сдвиг периметра
|
текст расшифровки начинает уходить на сторону — сдвиг периметра
|
||||||
[security.md](security.md).
|
[security.md](security.md).
|
||||||
- **Долгие записи.** Потолок сегодня неизвестен и не замерялся: 20 МиБ на приём
|
- **Долгие записи.** Потолок сегодня неизвестен и не замерялся: 20 МиБ на приём
|
||||||
из Telegram — точно, ограничения `deferred-general` по длине — нет. Расчётный
|
из Telegram — точно, ограничения `deferred-general` по длине — нет. Расчётные
|
||||||
потолок проекта — шесть часов, и он взят с запасом, а не замером.
|
шесть часов нормирует [storage](../openspec/specs/storage/spec.md), «Файл
|
||||||
|
записи живёт в хранилище»; откуда взято число —
|
||||||
|
[research/pocketbase-defaults.md](research/pocketbase-defaults.md), «Чего эта
|
||||||
|
записка не узнала».
|
||||||
- **Приём большого файла.** Форма читается целиком, предел памяти под multipart
|
- **Приём большого файла.** Форма читается целиком, предел памяти под multipart
|
||||||
задан числом в [database.md](database.md), «Настройки с числовым значением»;
|
задан числом в [database.md](database.md), «Настройки с числовым значением»;
|
||||||
обрыв начинает загрузку заново.
|
обрыв начинает загрузку заново.
|
||||||
@@ -218,8 +221,9 @@
|
|||||||
- **Очередь.** Модель очереди сделана задачей `pocketbase-storage` 2026-08-12
|
- **Очередь.** Модель очереди сделана задачей `pocketbase-storage` 2026-08-12
|
||||||
([ADR](adr/ADR-2026-08-11-queue-as-pocketbase-collection.md)) и нормирована
|
([ADR](adr/ADR-2026-08-11-queue-as-pocketbase-collection.md)) и нормирована
|
||||||
спекой `pipeline`. Не решено, отказываться ли от холостого опроса: три воркера
|
спекой `pipeline`. Не решено, отказываться ли от холостого опроса: три воркера
|
||||||
дают 259 200 запросов в сутки при нагрузке в единицы записей в день, и во что
|
дают 259 200 запросов к базе в сутки — расчёт из паузы воркера, а не замер
|
||||||
это обходится, никто не мерил.
|
([research/job-queue.md](research/job-queue.md), «Как снималось»), — при
|
||||||
|
нагрузке в единицы записей в день, и во что это обходится, никто не мерил.
|
||||||
- **Наблюдаемость.** `/metrics` остаётся и развивается. Чем — дописывать
|
- **Наблюдаемость.** `/metrics` остаётся и развивается. Чем — дописывать
|
||||||
счётчики через `client_golang` или перейти на OpenTelemetry с трассировкой —
|
счётчики через `client_golang` или перейти на OpenTelemetry с трассировкой —
|
||||||
решает разведка `opentelemetry-fit`. Коллектор был бы процессом, которого в
|
решает разведка `opentelemetry-fit`. Коллектор был бы процессом, которого в
|
||||||
|
|||||||
@@ -58,6 +58,11 @@
|
|||||||
лексикографическую сортировку TEXT, то есть хронологию (`ORDER BY created_at`).
|
лексикографическую сортировку TEXT, то есть хронологию (`ORDER BY created_at`).
|
||||||
Единая точка генерации — приложение, а не умолчание в схеме: так забытая
|
Единая точка генерации — приложение, а не умолчание в схеме: так забытая
|
||||||
вставка падает громко. Измерение длительности — не метка времени.
|
вставка падает громко. Измерение длительности — не метка времени.
|
||||||
|
*Расхождение:* вид времени задаёт хранилище — `2006-01-02 15:04:05.000Z`,
|
||||||
|
пробел вместо `T` и доли секунды ([../database.md](../database.md), «Время»).
|
||||||
|
Правило RFC 3339 действует на то, что пишем мы сами мимо хранилища; вид
|
||||||
|
хранилища не меняем — сравнение строк в сыром запросе побайтово, и
|
||||||
|
разошедшийся вид молча обращает условие срока захвата в константу.
|
||||||
- Миграции — шаги PocketBase на Go
|
- Миграции — шаги PocketBase на Go
|
||||||
(`internal/adapter/repo/pocketbase/migrations`, файл на шаг): коллекции и их
|
(`internal/adapter/repo/pocketbase/migrations`, файл на шаг): коллекции и их
|
||||||
поля заводятся кодом. При изменении структуры обновляем схему
|
поля заводятся кодом. При изменении структуры обновляем схему
|
||||||
|
|||||||
@@ -33,11 +33,13 @@
|
|||||||
- **Шрифты и скрипты — со своего хоста**, без внешних. Внешних ресурсов времени
|
- **Шрифты и скрипты — со своего хоста**, без внешних. Внешних ресурсов времени
|
||||||
выполнения нет.
|
выполнения нет.
|
||||||
- **Офлайн-чтения расшифровок и очереди отправки без сети не делаем** — граница
|
- **Офлайн-чтения расшифровок и очереди отправки без сети не делаем** — граница
|
||||||
цели [web-access](../../tasks/items/web-access.md). Без сети приложение
|
из [паспорта](../passport.md). Без сети приложение показывает состояние, а не
|
||||||
показывает состояние, а не пустой экран.
|
пустой экран.
|
||||||
- **Web Push не делаем**: уведомления идут через apprise и ntfy, цель
|
- **Web Push не делаем**: уведомления идут через apprise и ntfy — решение живёт
|
||||||
[ready-notification](../../tasks/items/ready-notification.md).
|
в [architecture.md](../architecture.md), «Уведомления», делает его
|
||||||
- **Записи звука в приложении не делаем** — файл выбирают системным диалогом.
|
[ntfy-delivery](../../tasks/items/ntfy-delivery.md).
|
||||||
|
- **Записи звука в приложении не делаем** — граница из
|
||||||
|
[паспорта](../passport.md), «Диктофон»; файл выбирают системным диалогом.
|
||||||
|
|
||||||
## Фреймворк и сборка
|
## Фреймворк и сборка
|
||||||
|
|
||||||
@@ -55,9 +57,12 @@
|
|||||||
|
|
||||||
## Маршруты
|
## Маршруты
|
||||||
|
|
||||||
- **Четыре экрана, одна таблица маршрутов** через `createRouter`. Маршруты по
|
- **Одна таблица маршрутов** через `createRouter`. Маршруты по файлам не
|
||||||
файлам не включаем: сборочная надстройка роутера пятой версии стоит 34 пакета
|
включаем: сборочная надстройка роутера пятой версии стоит 34 пакета в
|
||||||
в установке и на четырёх маршрутах не окупается.
|
установке и на нашем числе маршрутов не окупается
|
||||||
|
([research/spa-framework.md](../research/spa-framework.md), «Vue»). Сколько
|
||||||
|
экранов и какие — не здесь: состав нормирует спека приложения, а до неё его
|
||||||
|
держит [spa-skeleton](../../tasks/items/spa-skeleton.md).
|
||||||
- **Адреса обычные, а не после решётки** (`createWebHistory`). Отсюда требование
|
- **Адреса обычные, а не после решётки** (`createWebHistory`). Отсюда требование
|
||||||
к серверу: неизвестный путь **вне** `/api/` отдаёт `index.html`, а не `404`;
|
к серверу: неизвестный путь **вне** `/api/` отдаёт `index.html`, а не `404`;
|
||||||
пути внутри `/api/` в приложение не проваливаются никогда.
|
пути внутри `/api/` в приложение не проваливаются никогда.
|
||||||
|
|||||||
+2
-2
@@ -16,8 +16,8 @@ CGO сборке не нужен.
|
|||||||
|
|
||||||
Каталог у шагов свой, а не файл внутри пакета репозитория, и причина внешняя:
|
Каталог у шагов свой, а не файл внутри пакета репозитория, и причина внешняя:
|
||||||
шаг гейта сверяет изменённые шаги схемы с правкой этого документа по **префиксу
|
шаг гейта сверяет изменённые шаги схемы с правкой этого документа по **префиксу
|
||||||
пути** (`.av-dev.toml`, ключ `migrations` секции `[docs]`), а префикс наводится только на
|
пути**, а префикс наводится только на каталог. Где этот префикс задан —
|
||||||
каталог. Имена коллекций живут там же, рядом с шагом, который их заводит; пакет
|
[conventions/go-linters.md](conventions/go-linters.md), «Механизировано». Имена коллекций живут там же, рядом с шагом, который их заводит; пакет
|
||||||
репозитория берёт их оттуда.
|
репозитория берёт их оттуда.
|
||||||
|
|
||||||
**Идентификаторы** записей выдаёт хранилище — 15 знаков собственного алфавита.
|
**Идентификаторы** записей выдаёт хранилище — 15 знаков собственного алфавита.
|
||||||
|
|||||||
+6
-6
@@ -1,7 +1,7 @@
|
|||||||
# Паспорт проекта
|
# Паспорт проекта
|
||||||
|
|
||||||
Зачем это и для кого. [architecture.md](architecture.md) отвечает «как
|
Зачем это и для кого. [architecture.md](architecture.md) отвечает «как
|
||||||
устроено», [tasks/ROADMAP.md](../tasks/ROADMAP.md) — «в каком порядке», паспорт —
|
устроено», [tasks/BACKLOG.md](../tasks/BACKLOG.md) — «в каком порядке», паспорт —
|
||||||
«зачем и для кого».
|
«зачем и для кого».
|
||||||
|
|
||||||
## Цель
|
## Цель
|
||||||
@@ -32,8 +32,8 @@
|
|||||||
|
|
||||||
- запись любого распространённого формата принимается без предварительной
|
- запись любого распространённого формата принимается без предварительной
|
||||||
подготовки, включая дорожку из видео;
|
подготовки, включая дорожку из видео;
|
||||||
- запись длиной до шести часов доходит до текста, а не прерывается ошибкой при
|
- запись расчётного потолка — шести часов — доходит до текста, а не прерывается
|
||||||
достижении предела;
|
ошибкой при достижении предела (норма — `openspec/specs/storage`);
|
||||||
- сервисом пользуются несколько человек, и записи одного не видны другому;
|
- сервисом пользуются несколько человек, и записи одного не видны другому;
|
||||||
- текст доступен там же, где загружали, — в приложении и в Telegram. Человек
|
- текст доступен там же, где загружали, — в приложении и в Telegram. Человек
|
||||||
узнаёт о его готовности, не держа приложение открытым;
|
узнаёт о его готовности, не держа приложение открытым;
|
||||||
@@ -54,7 +54,8 @@
|
|||||||
- **Разговор о записи.** Ответы на вопросы по содержанию и поиск по смыслу — за
|
- **Разговор о записи.** Ответы на вопросы по содержанию и поиск по смыслу — за
|
||||||
границей. Заголовок, пересказ и темы **внутри** границы: она сдвинута
|
границей. Заголовок, пересказ и темы **внутри** границы: она сдвинута
|
||||||
2026-08-10, и до того запись читалась «мы отдаём текст, а не выводы из него».
|
2026-08-10, и до того запись читалась «мы отдаём текст, а не выводы из него».
|
||||||
Направление — цель [text-insights](../tasks/items/text-insights.md).
|
Считать уровни текста берётся задача
|
||||||
|
[llm-insights-adapter](../tasks/items/llm-insights-adapter.md).
|
||||||
- **Собственные модели.** Не обучаем и не держим у себя ни модель распознавания,
|
- **Собственные модели.** Не обучаем и не держим у себя ни модель распознавания,
|
||||||
ни языковую модель: и речь, и выводы из текста считает внешний сервис.
|
ни языковую модель: и речь, и выводы из текста считает внешний сервис.
|
||||||
- **Управление учётными записями.** Пользователей заводит и проверяет внешний
|
- **Управление учётными записями.** Пользователей заводит и проверяет внешний
|
||||||
@@ -65,8 +66,7 @@
|
|||||||
- **Живая расшифровка.** Работаем с готовой записью, поток в реальном времени не
|
- **Живая расшифровка.** Работаем с готовой записью, поток в реальном времени не
|
||||||
обрабатываем.
|
обрабатываем.
|
||||||
- **Диктофон.** Запись звука делает телефон, а приложение принимает готовый
|
- **Диктофон.** Запись звука делает телефон, а приложение принимает готовый
|
||||||
файл. Своей записи и работы без сети не делаем — граница цели
|
файл. Своей записи и работы без сети не делаем.
|
||||||
[web-access](../tasks/items/web-access.md).
|
|
||||||
- **Файловое хранилище общего назначения.** Храним аудио и видео, отданные ради
|
- **Файловое хранилище общего назначения.** Храним аудио и видео, отданные ради
|
||||||
речи в них. Складом произвольных файлов и папками сервис не становится. Общего
|
речи в них. Складом произвольных файлов и папками сервис не становится. Общего
|
||||||
доступа к чужим записям целью тоже нет — но **сегодня он есть**: владельца у
|
доступа к чужим записям целью тоже нет — но **сегодня он есть**: владельца у
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ Nuxt, Next — не рассматривали: конвенция
|
|||||||
правил, и в сборке он у всех троих совпал до байта (883 Б), что и подтверждает
|
правил, и в сборке он у всех троих совпал до байта (883 Б), что и подтверждает
|
||||||
одинаковость экрана.
|
одинаковость экрана.
|
||||||
- **Четыре маршрута** — тот же экран плюс три заглушки и переходы между ними:
|
- **Четыре маршрута** — тот же экран плюс три заглушки и переходы между ними:
|
||||||
столько экранов у цели [web-access](../../tasks/items/web-access.md). Роутеры
|
столько экранов заводит задача
|
||||||
|
[spa-skeleton](../../tasks/items/spa-skeleton.md). Роутеры
|
||||||
`svelte-spa-router` 5.1.1, `vue-router` 5.2.0 и 4.6.4, `react-router` 8.3.0.
|
`svelte-spa-router` 5.1.1, `vue-router` 5.2.0 и 4.6.4, `react-router` 8.3.0.
|
||||||
- **Размеры** — `stat -c%s` и `gzip -9c | wc -c` по файлам `dist/`. Числа Vite в
|
- **Размеры** — `stat -c%s` и `gzip -9c | wc -c` по файлам `dist/`. Числа Vite в
|
||||||
своём выводе печатает по другому уровню сжатия, поэтому в таблицах ниже стоят
|
своём выводе печатает по другому уровню сжатия, поэтому в таблицах ниже стоят
|
||||||
|
|||||||
+8
-4
@@ -151,8 +151,10 @@
|
|||||||
ещё — самый большой.
|
ещё — самый большой.
|
||||||
- `conventions`: новая колонка правится во всех четырёх местах репозитория
|
- `conventions`: новая колонка правится во всех четырёх местах репозитория
|
||||||
(CLAUDE.md, «Инварианты»).
|
(CLAUDE.md, «Инварианты»).
|
||||||
- `autotests`: покрыт ли изменённый шаг конвейера хоть одним тестом — сегодня
|
- `autotests`: покрыт ли изменённый шаг конвейера хоть одним **проходящим**
|
||||||
тестов два файла, и оба мимо конвейера.
|
тестом. Что уже закрыто проверками, видно по журналу дефектов ниже и по
|
||||||
|
[conventions/go-linters.md](conventions/go-linters.md), «Механизировано»;
|
||||||
|
числа файлов здесь не называем — оно протухает с каждой задачей.
|
||||||
- `autotests`: судит ли проверка ответа по готовому ответу, а не по изменяемому
|
- `autotests`: судит ли проверка ответа по готовому ответу, а не по изменяемому
|
||||||
состоянию обработчика — **только там, где ответ идёт мимо recorder**, через
|
состоянию обработчика — **только там, где ответ идёт мимо recorder**, через
|
||||||
свой `http.ResponseWriter`. Обращение к живой карте recorder'а с
|
свой `http.ResponseWriter`. Обращение к живой карте recorder'а с
|
||||||
@@ -188,8 +190,10 @@
|
|||||||
|
|
||||||
- вход через OIDC и разграничение доступа: как связаны пользователь Telegram и
|
- вход через OIDC и разграничение доступа: как связаны пользователь Telegram и
|
||||||
пользователь приложения, до начала работы назвать нельзя;
|
пользователь приложения, до начала работы назвать нельзя;
|
||||||
- всё, что делается на выбранном фреймворке впервые: форма решения нащупывается
|
- всё, что делается на выбранном фреймворке впервые: правила
|
||||||
по ходу, пока конвенция веб-UI пуста;
|
[conventions/web-ui.md](conventions/web-ui.md) выведены из выбора и из замера
|
||||||
|
на пробном экране, а не из написанного кода, и первая же задача проверяет их
|
||||||
|
собой — форма решения нащупывается по ходу;
|
||||||
- установка на телефон: service worker перехватывает запросы, и что он кэширует,
|
- установка на телефон: service worker перехватывает запросы, и что он кэширует,
|
||||||
до работы назвать нельзя;
|
до работы назвать нельзя;
|
||||||
- работа с записями в несколько часов: потолки внешних сервисов не замерены,
|
- работа с записями в несколько часов: потолки внешних сервисов не замерены,
|
||||||
|
|||||||
+5
-4
@@ -298,10 +298,11 @@ Telegram отправителю.
|
|||||||
не замер: распределения длин у сервиса нет, а самая длинная проверенная запись
|
не замер: распределения длин у сервиса нет, а самая длинная проверенная запись
|
||||||
— 9,6 МБ ([research/pocketbase-defaults.md](research/pocketbase-defaults.md)).
|
— 9,6 МБ ([research/pocketbase-defaults.md](research/pocketbase-defaults.md)).
|
||||||
Потолок длины стоит открытым вопросом `architecture.md`, «Долгие записи». Квот
|
Потолок длины стоит открытым вопросом `architecture.md`, «Долгие записи». Квот
|
||||||
нет и не будет: решено считать расход и показывать его владельцу, а не
|
нет — это граница домена, [passport.md](passport.md), «Учёт денег»; расход
|
||||||
отказывать (цель `usage-stats`). Перебравшего останавливает разговор или отзыв доступа в
|
считают `usage-accounting` и `admin-stats-screen`. Для модели угроз отсюда
|
||||||
Authelia. Рост каталога данных при этом ничем не наблюдается —
|
следует одно: ни числом запросов, ни размером записи вошедший не ограничен, и
|
||||||
открытый вопрос `architecture.md`.
|
защищаться от исчерпания диска мы не пытаемся. Рост каталога данных при этом
|
||||||
|
ничем не наблюдается — открытый вопрос `architecture.md`.
|
||||||
- **Перерасход денег на внешних сервисах.** Распознавание и языковая модель
|
- **Перерасход денег на внешних сервисах.** Распознавание и языковая модель
|
||||||
оплачиваются по факту; потолка на пользователя нет по тому же решению.
|
оплачиваются по факту; потолка на пользователя нет по тому же решению.
|
||||||
- **Стойкость `ffmpeg` к вредоносному входу.** Разбор чужого формата отдан
|
- **Стойкость `ffmpeg` к вредоносному входу.** Разбор чужого формата отдан
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ context: |
|
|||||||
- docs/architecture.md — устройство; docs/security.md — периметр;
|
- docs/architecture.md — устройство; docs/security.md — периметр;
|
||||||
docs/database.md — схема и настройки с числами; docs/adr/ — почему решено
|
docs/database.md — схема и настройки с числами; docs/adr/ — почему решено
|
||||||
так; docs/research/ — что уже измерено;
|
так; docs/research/ — что уже измерено;
|
||||||
- tasks/ROADMAP.md — что приложение уже умеет и чего ещё не умеет.
|
- openspec/specs/ — что приложение уже умеет; tasks/BACKLOG.md — что осталось
|
||||||
|
и в каком порядке это берут.
|
||||||
Пересказа этих документов здесь нет намеренно: второй дом факта расходится с
|
Пересказа этих документов здесь нет намеренно: второй дом факта расходится с
|
||||||
первым молча, и заметно это становится в предложении, которое уже написано.
|
первым молча, и заметно это становится в предложении, которое уже написано.
|
||||||
|
|
||||||
|
|||||||
@@ -5,14 +5,14 @@
|
|||||||
Конвейер расшифровки: как задача движется по состояниям, что делает воркер,
|
Конвейер расшифровки: как задача движется по состояниям, что делает воркер,
|
||||||
когда работы нет, и что считается отказом шага.
|
когда работы нет, и что считается отказом шага.
|
||||||
|
|
||||||
Описан пока **только пустой прогон воркера** — тот, что нормируют проверки
|
Описаны пустой прогон воркера, неделимость захвата и срок его протухания, число
|
||||||
пакета `internal/controller/worker` и перевод признака в `internal/service`.
|
попыток и состояние «мертва», нарастающая пауза перед повтором и условие записи
|
||||||
Сознательно не описаны переходы состояний и цепочка `created → converted →
|
результата держателем захвата. Сознательно не описаны: цепочка переходов
|
||||||
transcribe → done | failed`, захват задачи и срок его протухания, отмена
|
`created → converted → transcribe → done | failed`, отмена контекста посреди
|
||||||
контекста посреди шага, освобождение ресурсов внешних клиентов. Это не значит,
|
шага и освобождение ресурсов внешних клиентов. Это не значит, что такого
|
||||||
что такого поведения нет: оно живёт в коде, а требования на него не написаны,
|
поведения нет: оно живёт в коде, а требования на него не написаны, потому что
|
||||||
потому что требование без проверки — предположение, а не норма. Первая задача,
|
требование без проверки — предположение, а не норма. Первая задача, которая
|
||||||
которая трогает любое из перечисленного, дописывает его сюда.
|
трогает любое из перечисленного, дописывает его сюда.
|
||||||
## Requirements
|
## Requirements
|
||||||
### Requirement: Пустой прогон воркера — не отказ
|
### Requirement: Пустой прогон воркера — не отказ
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user