Files
orchestrator/docs/work-items/ORCH-057/02-trz.md

11 KiB
Raw Blame History

work_item, stage, author_agent, status, created_at, model_used
work_item stage author_agent status created_at model_used
ORCH-057 analysis analyst ready-for-review 2026-06-10 claude-opus-4-8

02 — ТЗ (TRZ): ORCH-057 — нормализация legacy root-owned файлов при миграции на uid 1000

Work Item: ORCH-057 · Repo: orchestrator · Стадия: analysis

ТЗ описывает конкретные изменения к реализации, выведенные из BRD и фактического кода. Архитектурное обоснование/выбор варианта one-time нормализации (init-контейнер vs blocking-entrypoint vs ансибл) — задача архитектора (06-adr/). Здесь — требования, контракты и ограничения.

1. Сводка изменения

Закрыть недоделанный AC ORCH-040 по legacy-файлам. Три слоя:

  1. Защита launcherensure_worktree распознаёт Permission denied/git-fatal на создании worktree и поднимает внятную ошибку с диагнозом «legacy root-файлы в /repos/_wt — нужна нормализация прав» + лечащая команда (опц. авто-самолечение при наличии прав).
  2. Ранний детект — новый чистый леаф находит файлы с uid != target_uid в ORCH_REPOS_DIR (_wt, .git/objects, .git/worktrees, data/runs); вызывается на старте сервиса и/или перед claim'ом job; never-raise, config-gated, с наблюдаемостью.
  3. ПроцедураINFRA.md + ADR ORCH-057: точные команды разовой нормализации как обязательный шаг миграции uid. Опционально — one-time нормализация под root через init-механизм (решает архитектор).

Инвариант: STAGE_TRANSITIONS / QG_CHECKS / check_* / machine-verdict-ключи / схема БД — байт-в-байт прежние. Изменение аддитивно и обратимо kill-switch'ем.

2. Задействованные модули / пути

Путь Действие
src/git_worktree.py (ensure_worktree, remove_worktree) изменить — классификация Permission denied/git-fatal на git worktree add / os.makedirs → внятный actionable RuntimeError (опц. self-heal при правах)
src/fs_normalize.py создать — чистый леаф (never-raise): scan_ownership(roots, target_uid) -> результат; опц. normalize(...) (chown только при наличии прав); хелпер applies(repo) + кэш (TTL, как preflight)
src/config.py изменить — добавить флаги (см. §7); без правки существующих значений
src/main.py (lifespan) изменить — добавить startup-вызов детекта (best-effort, never-fatal по образцу L-2/lease-reclaim), лог + Telegram при mismatch; read-only блок в GET /queue
src/preflight.py или src/queue_worker.py изменить (на выбор архитектора) — опц. гейт claim'а job при обнаруженном mismatch, чтобы падать внятно ДО launch (по образцу preflight-гейта)
docker-compose.yml / Dockerfile / scripts/*entrypoint* кандидат (решает архитектор) — one-time root-нормализация (init-контейнер/хук) ПЕРЕД стартом app; если выбрано — деплой self, обязательная staging-страховка
docs/operations/INFRA.md изменить — раздел «Миграция uid: обязательная нормализация legacy root-файлов» (команды + область)
docs/work-items/ORCH-057/06-adr/ADR-001-*.md создать (architect) — решение + процедура; опц. forward-breadcrumb из ADR-001 ORCH-040 (без переписывания истории)
CHANGELOG.md изменить — запись о ORCH-057
tests/test_* создать — см. 04-test-plan.yaml

3. Функциональные требования

FR-1 — Внятная ошибка ensure_worktree (BR-1, BR-5)

При неуспехе git worktree add / os.makedirs(os.path.dirname(wt)) по причине отказа доступа (Permission denied, could not create leading directories, insufficient permission for adding an object) ensure_worktree поднимает RuntimeError с сообщением, которое: (а) называет корневую причину (legacy root-owned файлы в /repos/_wt или .git после миграции uid ORCH-040); (б) указывает лечащую команду (chown -R <uid>:<gid> …) или ссылку на процедуру INFRA.md; (в) НЕ является сырым git stderr. Прочие (нет-прав-несвязанные) ошибки сохраняют текущий контракт (никакой подмены смысла).

FR-2 — Детект несоответствия владельца (BR-2, BR-4)

Леаф fs_normalize.scan_ownership обходит корни (/repos/_wt, <repo>/.git/objects, <repo>/.git/worktrees, data/runs) и возвращает: есть ли файлы с uid != target_uid, их число (или флаг «≥1»), список затронутых корней. Обход дешёвый/ограниченный (ранний выход при первом mismatch для быстрого вердикта; полный подсчёт — опционально/семплировано). Результат кэшируется по TTL (по образцу preflight._cache). target_uid = os.getuid() или конфиг (дефолт 1000).

FR-3 — Реакция на детект (BR-1, BR-4)

  • Startup (main.lifespan): вызвать детект best-effort; при mismatch — структурный WARNING + Telegram (если включён) с числом/корнями и лечащей командой. Никогда не падать на старте по ошибке детекта (NFR-3).
  • Опц. гейт claim'а: при обнаруженном mismatch и target_uid без прав на chown — не претендовать на job (или претендовать и сразу честно фейлить с FR-1-сообщением), чтобы исход был внятным до launch. Конкретную точку (preflight vs queue_worker) выбирает архитектор; требование — «внятно и заранее».

FR-4 — Опциональная авто-нормализация (BR-1)

fs_normalize.normalize выполняет chown -R target_uid:target_gid по корням только если процесс имеет на это право (CAP_CHOWN/root). Под uid 1000 без прав — no-op + честный лог «нужна операторская процедура» (НЕ ошибка). Включается отдельным флагом (*_AUTO), по умолчанию — выкл (детект-only). Если архитектор выбирает init-контейнер под root — это и есть носитель FR-4 на буте.

FR-5 — Документированная процедура (BR-3)

INFRA.md получает раздел с точными командами разовой нормализации (_wt, оба .git, data/runs), помеченный как обязательный шаг миграции uid и часть чеклиста деплоя self. ADR ORCH-057 фиксирует решение и ссылается на процедуру; ADR-001 ORCH-040 опц. получает forward-ссылку.

4. Изменения API

Нет новых обязательных эндпоинтов. Опционально (наблюдаемость, решает архитектор):

  • расширить GET /queue read-only блоком fs_ownership ({enabled, target_uid, mismatch, roots, checked_at});
  • ручной триггер POST /fs-normalize/check (форс-пересчёт детекта) — по образцу POST /serial-gate/unfreeze.

5. Изменения схемы БД

Нет. Состояние детекта — в памяти (TTL-кэш), как preflight. Таблицы/миграции/индексы не вводятся.

6. Требования к новым/изменённым QG checks

Нет. Это не stage-гейт и не под-гейт ребра. QG_CHECKS / check_* / STAGE_TRANSITIONS / machine-verdict-ключи (verdict:/result:/deploy_status:/staging_status:/security_status:/ coverage_status:) — не трогаются. (В описании баг-репорта «deploy-гейт ORCH-040» — это деплой-хук/ процедура, а не зарегистрированный QG.)

7. Совместимость / регресс

  • Kill-switch ORCH_FS_NORMALIZE_ENABLED (дефолт по решению архитектора; False → весь код инертен, поведение 1:1 как до ORCH-057).
  • Scope ORCH_FS_NORMALIZE_REPOS (CSV; пусто → self-hosting only, как coverage_gate_repos → enduro-trails не затронут). Локальный applies(repo) проверяется ПЕРВЫМ (дешёвый обход только при applies).
  • Флаги (рабочие имена, финал — за архитектором): ORCH_FS_TARGET_UID (дефолт 1000), ORCH_FS_NORMALIZE_AUTO (дефолт False — детект-only; True → попытка chown при наличии прав), ORCH_FS_SCAN_ROOTS (CSV переопределения корней), ORCH_FS_SCAN_CACHE_TTL_S.
  • Never-raise / fail-safe — ошибка детекта/нормализации деградирует в WARNING, не блокирует старт сервиса по своей вине; FR-1 меняет лишь формулировку ошибки worktree, не её факт.
  • Self-hosting (NFR-1) — код только читает/детектит/диагностирует (и chown ТОЛЬКО при наличии прав); не деплоит/не рестартит прод/не трогает main. Любое касание docker-compose.yml/entrypoint требует staging-прогона (8501) перед прод-рестартом в окно тишины.
  • Обратимость — выкл kill-switch → прежнее поведение; миграций/правки схемы нет.
  • Пайплайн-артефакты: обновляются 01..04 (analysis), 06-adr/+07-infra-requirements.md+10-tech-risks.md (architecture), 12/13/15/14 (review/testing/staging/deploy), INFRA.md, CHANGELOG.md.