diff --git a/AGENTS.md b/AGENTS.md index 5e19ba0..0c36664 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -41,6 +41,31 @@ Python, SQLite и OpenSearch. На 2026-10-25 публичный `/review` и п - После каждого изменения инфраструктуры обновляй `docs/operations/CURRENT_STATE.md` по фактическим проверкам. +## Delivery First: проверка по уровню риска + +- Перед работой укажи в PR уровень `LOW`, `MEDIUM` или `HIGH`; при сомнении + выбери более высокий. Уровень добавляет нужную проверку, но никогда не + отменяет CI, одобрение или защиту `main`. +- Каждый PR сохраняет текущие проверки Gitea Actions: job IDs + `tests-and-images` и `runner-smoke`. Не переименовывай и не обходи их, не + удаляй тесты или проверки безопасности ради скорости. +- `LOW`: документация, комментарии и тексты без изменения исполняемого + поведения или контракта; проверяй diff и актуальность утверждений, затем + дождись обычного CI. +- `MEDIUM`: изменение поведения в одном компоненте; добавь регрессионный тест, + выполни целевые проверки и обычный CI. +- `HIGH`: доступ/авторизация, данные и ingestion, схемы/хранилища, + backup/restore/recovery, production config, секреты, runner/CI, безопасность + или deploy; выполни целевые тесты вместе с обычным CI и приложи проверяемое + обоснование угроз, совместимости и rollback. Production-действия остаются + за владельцем. +- Ревью блокирует только конкретный воспроизводимый дефект, нарушение границы + доступа/безопасности, несовместимость данных или контракта, пропущенную + обязательную проверку либо отсутствие безопасного rollback. Идеи и стилистические + замечания перечисляй отдельно как неблокирующие. +- Полная процедура, метрики, Gitea-ограничения и проверенные временные бюджеты: + [DELIVERY_POLICY.md](docs/development/DELIVERY_POLICY.md). + ## Автономный workflow PR - Маршруты ниже указаны из контекста оркестратора/root: оркестратор — thread diff --git a/CHANGELOG.md b/CHANGELOG.md index 655e4ad..2458c74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ - Frontend `0.3.3` / Backend `0.13.2`: управление пользователями отображается согласно правам аккаунта, подписи ролей локализованы, повторяющиеся действия кабинета убраны, а знак лаборатории приведён к основному бренду. +- Добавлена политика Delivery First по уровням `LOW`/`MEDIUM`/`HIGH`: описаны + обязательная глубина проверок, регрессионные тесты и доказательства угроз, + совместимости и отката; обычные CI, одобрение и защита `main` остаются + обязательными. - Frontend `0.3.2`: устранены пересечения названия бренда с поиском и мобильной навигации документа; навигационные ссылки получили общие quiet и primary состояния. diff --git a/docs/development/DELIVERY_POLICY.md b/docs/development/DELIVERY_POLICY.md new file mode 100644 index 0000000..ed01a51 --- /dev/null +++ b/docs/development/DELIVERY_POLICY.md @@ -0,0 +1,177 @@ +# Delivery First: проверка изменений по риску + +Эта политика помогает быстро принимать небольшие изменения и добавляет +проверки там, где ошибка затрагивает пользователей, данные или границы +безопасности. Класс риска выбирает глубину проверки; он не позволяет снять +обязательную проверку, одобрение или защиту `main`. + +## Порядок работы + +1. До реализации укажи в PR `LOW`, `MEDIUM` или `HIGH` и коротко объясни + причину. Если изменение пересекает несколько областей, выбери самый высокий + класс. При неопределённости используй более высокий класс. +2. Сначала проследи путь изменения от пользовательского/API входа до данных, + внешних систем и отката. Для изменения поведения добавь регрессионный тест, + который подтверждает конкретный контракт или прежний дефект. +3. Запусти проверку именно затронутого поведения и обычный Gitea CI. Для UI + дополнительно выполни `cd tools/frontend-visual-review && npm ci && npm test`; + этот браузерный набор сейчас не входит в Gitea CI. +4. В PR перечисли класс риска, команды и результаты, остаточные ограничения, + а для high-risk — совместимость, угрозы и безопасный rollback. Не заявляй + проверку, которой фактически не было. +5. Ревьюер отделяет блокирующие дефекты от улучшений. Блокер должен указывать + конкретное место, условие воспроизведения и последствие. Стилистические + предпочтения и предложения на будущее фиксируются отдельно и не задерживают + PR. + +## Классы риска + +| Класс | Когда применять | Требуемое подтверждение | +|---|---|---| +| `LOW` | Документация, комментарии, переименование без изменения поведения, форматирование без изменения конфигурационной семантики. Нет изменения контракта, доступа, данных или исполнения. | Проверить diff и сверить утверждения/команды с исходным кодом и действующей процедурой. Выполнить обычный CI. Если документация меняет операторский шаг, проверить его в безопасном локальном или synthetic окружении. | +| `MEDIUM` | Ограниченное изменение поведения одного компонента, внутреннего API, UI или локального скрипта без изменения модели доверия и формата хранимых данных. | Целевые тесты затронутого компонента и регрессионная проверка, если меняется поведение; обычный CI. Для UI приложить результаты браузерного набора и synthetic визуальной проверки, если они относятся к изменённому экрану/потоку. Проверить обратную совместимость публичных запросов и ответов. | +| `HIGH` | Авторизация и роли; secrets и credential flow; ingestion и происхождение данных; формат/миграция хранилища; backup/restore/recovery; production config; runner, CI и supply chain; безопасность; сетевые/host-границы; deploy и rollback. | Обычный CI плюс целевые проверки каждого затронутого trust/data boundary; regression-тест; проверка негативного пути и совместимости; независимое ревью конкретных угроз и последствий. Для операций с данными использовать synthetic/staging данные и план отката. Изменение production допускается только владельцем после отдельного согласования, проверки backup и готового rollback. | + +Нельзя понижать класс из-за малого diff, срочности или простоты реализации: +маленькое изменение правила доступа или восстановления всё равно `HIGH`. +Нельзя считать отсутствие найденных тестов доказательством отсутствия риска. + +## CI и ограничения Gitea + +Проверенная конфигурация проекта: + +- Репозиторий `admin/akyldash`, Gitea API вернул `1.25.5` 2026-10-24. +- Workflow `.gitea/workflows/ci.yaml` запускается на `pull_request` и `push` в + `main`; job ID, который следует сохранять для совместимости статусов, — + `tests-and-images`. + Он последовательно выполняет backend, Telegram, backup/restore, shell guards, + deployment tests, Compose/Caddy validation и две production image builds. +- `.gitea/workflows/runner-smoke.yaml` сохраняет отдельный job ID `runner-smoke` + и проверяет runtime-изоляцию job-контейнера: отсутствие системного и + rootless Docker socket, production env/secrets и host checkout. Smoke дополняет + статические проверки runner/CI и не дублирует их. +- Инфраструктурная конфигурация репозитория закрепляет официальный + `act_runner 0.2.13`, label `akyldash-ci`, capacity 1 и ограничения job 2 CPU, + 3 GiB RAM, 512 процессов, 15 GiB store. Версия runner и лимиты приведены по + IaC; в рамках этой задачи VPS не проверялся. Job-контейнеры не должны получать + ни один Docker socket. +- Текущая политика `main` из branch API: ветка защищена и требует одного + одобрения. На момент проверки API также сообщил `enable_status_check=false` + и пустой список status contexts. Это PR не меняет branch protection. + Если владелец отдельно включит обязательные статусы, сначала проверьте их + точные контексты: `CI / tests-and-images (pull_request)` и + `Runner smoke test / runner-smoke (pull_request)`. Не переименовывайте job IDs. + +Функции и границы текущих проверок: + +| Блок workflow | Назначение и риск пропуска | Перекрытие | +|---|---|---| +| Checkout и Python setup | Подготовка повторяемой среды; пропуск может дать ложный успех на неподходящем образе. | Инфраструктурный шаг, не покрывается тестами приложения. | +| Backend и Telegram tests | Регрессии API, логики поиска/аккаунтов и Telegram bot. Риск продуктовый; для auth/data изменений — `HIGH`. | Разные пакеты и контракты; отдельная ветка тестов. | +| Backup/restore tests | Целостность backup, восстановление и отказные сценарии; риск потери данных — `HIGH`. | Не дублируется тестами backend. | +| Bootstrap/recovery/verifier/host-audit/production-config guards | Отказ от root и защиты host identity, аргументов и production paths; обход может дать административное действие или затронуть host — `HIGH`. | Несколько guard-тестов проверяют разные скрипты и условия; считать их дублями без анализа каждого контракта нельзя. | +| Runner config, installer guards и CI container isolation | Ограничения runner, repeatability установки и запрет socket/privileged/host mounts; риск — доступ к host/production — `HIGH`. | Статические/синтетические проверки дополняются реальным runtime smoke, это разные уровни. | +| Shell syntax и Python AST parse | Раннее обнаружение ошибок парсинга; риск пропуска — поломка запуска скрипта/проверки. | Это синтаксис, а не функциональные тесты. | +| Deployment contract/rollback tests, Compose config и Caddy validation | Совместимость deploy contract/rollback и конфигурации production stack/proxy; риск — deploy outage/data impact — `HIGH`. | Compose проверяет интерпретацию конфигурации, Caddy — синтаксис proxy, Python suite — contract/rollback logic. Функции отличаются. | +| Две Kaniko image builds | Проверка Dockerfile и воспроизводимости backend/OpenSearch production images; риск — сломанная сборка/небезопасный путь supply chain — `HIGH`. | Два Dockerfile — две разные целевые images; build-only, оба `--no-push`. | +| Runner smoke (отдельный workflow) | Runtime-проверка известного системного socket path, production paths/secrets и host checkout отсутствия в job; риск — нарушение изоляции — `HIGH`. | Дополняет статические runner/CI guards; не повторяет их. | + +Gitea API не предоставляет пригодный список steps для завершённого run в этой +версии; длительность каждой строки таблицы поэтому `N/A`. Не распределяй общий +CI runtime между блоками пропорционально числу тестов или строк кода. + +Gitea 1.25 документация подтверждает используемые события `push`, +`pull_request` и `workflow_dispatch`. Она также документирует ограничения, +которые запрещают строить безопасное ускорение на привычных GitHub Actions +предположениях: + +- `runs-on` поддерживает статическую метку или список меток; текущая + `akyldash-ci` — статическая метка. +- Выражения доступны ограниченно; в сравнении с GitHub Actions Gitea 1.25 + обещает функцию `always()`, поэтому workflow не должен зависеть от + `if:`-выражений для динамического выбора набора тестов по diff или уровню + риска. +- `jobs..timeout-minutes`, `permissions`, `concurrency` и + `jobs..environment` в Gitea 1.25 игнорируются. Не используйте их как + защиту, срок исполнения, обязательный approval или средство экономии. +- У runner capacity 1. Разделение текущей последовательной работы на + параллельные jobs не даёт измеримого параллельного исполнения на этом runner + и может добавить запуск/очередь. Сохраняйте проверку сборки обеих production + images: она подтверждает Dockerfiles, но использует Kaniko с `--no-push` и не + публикует образы. + +Источники семантики: [Gitea 1.25 Actions comparison](https://docs.gitea.com/1.25/usage/actions/comparison/), +[Gitea 1.25 Actions FAQ](https://docs.gitea.com/1.25/usage/actions/faq/) и +[act_runner guide](https://docs.gitea.com/1.25/usage/actions/act-runner/). +Перед использованием новой workflow-семантики сверяй её с документацией +установленной версии и проверяй фактическим run. Не добавляй проверки самой +этой политики или статические тесты, утверждающие её текст. + +## Временные ориентиры и фактические измерения + +Ориентиры — цель для поиска затрат, а не разрешение убрать тесты: + +- Обычный полный CI: до 5 минут от старта job до завершения. При превышении + сначала измерить и оптимизировать узкий участок, сохраняя тесты и изоляцию. +- Расширенный путь для `HIGH` (обычный CI плюс целевые проверки): целевой + суммарный интервал 10–15 минут. Это пока ориентир, а не отдельный + автоматический workflow или установленный timeout. + +Срез 2026-10-24 получен из документированного [Gitea Actions task API](https://docs.gitea.com/api/1.25/operations/list-action-tasks/) +`/repos/admin/akyldash/actions/tasks?limit=50`. Взяты 20 последних завершённых +`tests-and-images`; длительность оценена как `updated_at - run_started_at`: + +| Метрика | Измерение | +|---|---:| +| CI completed sample | 20 прогонов, 20 success | +| CI median | 147.5 s (2:27.5) | +| CI p90 | 172 s (2:52) | +| CI max | 192 s (3:12) | +| Runner smoke median / max | 2 s / 3 s, последние 20 прогонов | + +Прежней reconciliation или оценок длительности в доступной документации +репозитория, workflows, PR и issues не найдено; этот срез является первой +подтверждённой базой, и сравнить его с прежней оценкой нельзя. +Все 20 измеренных полных CI прогонов ниже пяти минут; текущая конфигурация уже +достигает обычного ориентира. Отдельного extended workflow нет, поэтому +10–15-минутная цель пока не имеет фактической базы. В выборке нет breakdown по +steps: документированный endpoint для jobs завершённого run возвратил пустой +список; это известное ограничение Gitea 1.25.5 ([upstream issue #38286](https://github.com/go-gitea/gitea/issues/38286)). +Не распределяй время между тестами и image builds по предположению. При будущих +изменениях workflow записывай доступные timestamps и точный размер выборки; +после изменения Gitea/runner перепроверь, стал ли доступен надёжный step timing. + +## Метрики потока поставки + +Для каждого релиза/месячного обзора отдельно собирать: + +| Метрика | Определение и источник | +|---|---| +| Ready → merge | Время с момента, когда PR реально готов к ревью, до merge. Не подменять датой создания draft PR; при отсутствии точного ready timestamp записывать `N/A`. | +| Merge → deploy | Merge timestamp до фактического production deploy timestamp. Deploy пока не автоматизирован; пока нет подтверждённого deploy evidence, записывать `N/A`, не ноль. | +| CI duration | `run_started_at` → task completion (`updated_at`) по Actions API; отдельно указывать медиану, p90, максимум, период и число прогонов. | +| Повторные прогоны | Число неуспешных/повторных прогонов на commit до первого успеха; отличать rerun от нового commit/push. | +| Блокирующие замечания | Число ревью-находок, удовлетворяющих критериям blockers выше; неблокирующие считать отдельно. | +| Вышедшие регрессии | Подтверждённые после merge дефекты, связанные с изменением; фиксировать компонент, тяжесть и пропущенный сигнал. | +| Rollback | Количество фактически выполненных rollback, причина и результат проверки данных/сервиса. Не считать написанный план фактическим rollback. | + +На момент этого аудита PR/API не дают надёжного ready timestamp для старых +изменений; production deployment не автоматизирован, а задачи этого PR не +содержат deploy telemetry. Поэтому ready→merge, merge→deploy, escaped +regressions и rollback baseline остаются `N/A`, пока не появится проверяемая +запись. Не придумывай значения из `created_at` или даты последнего update. + +## Критерий блокирующего ревью + +Блокирующие замечания ограничены случаями: + +- воспроизводимый дефект с конкретным условием и последствием; +- расширение доступа, утечка секрета или обход границы runner/production; +- потеря/искажение данных или несовместимость API/storage без безопасного пути; +- обязательный тест или подтверждение из таблицы риска отсутствует/не проходит; +- опасная операция не имеет проверяемого backup/rollback плана. + +Каждый блокер содержит файл/участок, шаги воспроизведения или точное условие, +последствие и минимальный безопасный fix. Остальные замечания — отдельный +неблокирующий список. Review и approval остаются за людьми; агент не начинает +review сам и не сливает PR.