# Очередь uNews

## Назначение

uNews автоматически просматривает все публичные репозитории `sunpole`. Приватные репозитории не запрашиваются и не публикуются.

## Порядок обработки

1. В проекте появляется `news/*.md` с изображением.
2. Патчноут получает обязательные `version` и `queued_at`.
3. uNews проверяет YAML, текст, ссылку и правила безопасности.
4. Каждое изображение скачивается через GET и проходит byte-level проверку формата, размера и внутренней структуры.
5. Для каждого проекта выбирается самая ранняя версия.
6. Среди готовых проектов выбирается запись с самым ранним `queued_at`.
7. Перед Telegram выбранное изображение скачивается и проверяется повторно.
8. Telegram получает проверенные bytes как multipart Blob, а не исходный raw URL.
9. За запуск публикуется до 20 готовых новостей в строгом FIFO-порядке.
10. Сразу после ответа Telegram результат атомарно записывается в `data/published.json`.
11. После каждого ответа Telegram `data/published.json` немедленно коммитится и отправляется в `main`.
12. После пакета обновляются `data/health.json` и `data/errors.json`.

Полный технический контракт изображений: [IMAGE_INTEGRITY.md](IMAGE_INTEGRITY.md).

## Пауза и checkpoint

Внутри пакета между Telegram-запросами выдерживается не меньше 61 секунды. Это намеренно намного медленнее официального технического ограничения одного сообщения в секунду для одного чата и визуально разносит посты по разным минутам.

Пакет ограничен 20 постами. После каждого поста состояние отправляется в `main`; если checkpoint не прошёл, пакет немедленно останавливается. Оставшаяся очередь ждёт следующего планового или ручного запуска.

Git identity `github-actions[bot]` настраивается до publisher-step, поэтому первый per-post checkpoint не зависит от более позднего fallback-коммита состояния.

## Ошибки и блокировка

Если ранняя новость проекта ошибочна, следующие версии этого проекта блокируются. Другие проекты продолжают обслуживаться.

Изображение блокируется до Telegram, если:

- URL не отвечает успешным GET;
- расширение не совпадает с реальным форматом;
- превышен лимит размера;
- повреждена PNG/JPEG/GIF/WebP структура;
- PNG имеет неверный chunk CRC;
- PNG IDAT не декодируется;
- размеры или пропорции выходят за допустимые границы.

При фатальном сбое recovery-runner сохраняет безопасную причину. Workflow сначала коммитит изменённый state, затем возвращает ошибку.

## Расписание

Плановая проверка выполняется раз в четыре часа на 7-й минуте: `7 */4 * * *` (`00:07`, `04:07`, `08:07`, `12:07`, `16:07`, `20:07 UTC`). Это 2 190 плановых проверок в год вместо 52 560 при десятиминутном polling.

Для срочной новости используется ручной запуск `workflow_dispatch` с `dry_run=false`. Дополнительный PAT не нужен.

Саморедактирование cron намеренно не используется: GitHub запрещает штатному Actions App изменять workflow без расширенного `workflows` permission. Хранить ради этого PAT с широкими правами было бы менее безопасно, чем простое фиксированное расписание.

## Контроль

- последний запуск берётся из GitHub Actions;
- опубликованные ключи, Telegram `message_id` и `post_url` хранятся в `data/published.json`;
- состояние очереди хранится в `data/health.json`;
- ошибки хранятся в `data/errors.json`;
- при сбое или заблокированном проекте создаётся один GitHub Issue;
- после восстановления служебный Issue закрывается автоматически.

## Секреты

Перед каждым реальным запуском выполняются безопасные `getMe` и `getChat`. Значения токена и идентификатора канала не печатаются. Telegram bot token не имеет заданного календарного срока: действительность проверяется ответом API.
