--- work_item: ORCH-057 stage: analysis author_agent: analyst status: ready-for-review created_at: 2026-06-10 model_used: claude-opus-4-8 --- # 01 — BRD (бизнес-требования): ORCH-057 — нормализация legacy root-owned файлов при миграции на uid 1000 (one-time + защита) Work Item: **ORCH-057** · Repo: **orchestrator** · Стадия: analysis ## 1. Бизнес-контекст и проблема ORCH-040 перевёл оба контейнера (`orchestrator` 8500, `orchestrator-staging` 8501) с root на `user: "1000:1000"` (slin). Изменён был **только** `docker-compose.yml`. Однако bind-mount `/home/slin/repos → /repos` уже содержал файлы и каталоги, созданные **прежним root-контейнером** (`root:root`). Смена `user:` владельца существующих файлов НЕ меняет. **Реальный инцидент (прод, 06.06, поймали на первом запуске ORCH-043).** Первый job под uid 1000 упал на стадии **launch** (НЕ на коде задачи): ``` fatal: could not create leading directories of '/repos/_wt/orchestrator/feature_ORCH-043-.../.git': Permission denied ``` Причина: `/repos/_wt/` и старые worktree-папки = `root:root` → uid 1000 не может создать рядом новый каталог worktree. Установлено фактически: ошибка возникает в `src/git_worktree.py::ensure_worktree` (вызов `git worktree add`), куда конвейер приходит из `src/agents/launcher.py::_spawn` (стр. 500) и `_materialize_deferred_branch` (ORCH-088). Агент даже не стартует — падает создание worktree. **Ручной workaround (применён Стрим, прод снова рабочий, ОДНОРАЗОВО):** ``` sudo chown -R 1000:1000 /home/slin/repos/_wt sudo chown -R 1000:1000 /home/slin/repos/orchestrator/.git /home/slin/repos/enduro-trails/.git sudo chown -R 1000:1000 /home/slin/repos/orchestrator # +data/runs/*.log (37 root-логов) ``` ADR-001 ORCH-040 упоминал «массовый chown старых root-файлов» лишь абстрактно («вне объёма кода», «разовая операция Owner») и НЕ дал конкретной процедуры чистки legacy worktree — поэтому deployer её не выполнил, и баг проявился в проде. Прод сейчас рабочий (ручной фикс наложен), но проблема **воспроизведётся** на чистой среде, новом репо или после любого исторического запуска под root, если её не закрыть кодом + процедурой. **Это follow-up / закрытие недоделанного AC ORCH-040** (legacy-файлы), а не новая фича. ## 2. Объём (scope) ### В объёме - **Защита launcher (код):** при `Permission denied` на создании worktree выдавать **внятную, диагностируемую** ошибку «legacy root-файлы в `/repos/_wt` — требуется нормализация прав» с указанием команды, а НЕ сырой `git fatal`. - **Раннее обнаружение (код):** детектирование наличия файлов с `uid != ` в `ORCH_REPOS_DIR` (включая `_wt`, `.git/objects`, `.git/worktrees`, `data/runs`) при старте контейнера / перед претензией на job — чтобы конвейер падал **внятно и заранее**, а не сырым git-фаталом на launch. - **Процедура нормализации (документация):** в `docs/operations/INFRA.md` (и собственный ADR ORCH-057) — обязательная одноразовая процедура нормализации legacy root-файлов при миграции uid, с точными командами и областью охвата (`_wt`, `.git`, `data/runs`). - **Опционально (по решению архитектора):** механизм one-time нормализации при буте/деплое — init-контейнер/хук под root, либо blocking-entrypoint-проверка. ### Вне объёма - Изменение логики конвейера, `STAGE_TRANSITIONS`, `QG_CHECKS`, `check_*`, схемы БД. - Пересмотр самого решения ORCH-040 (uid 1000) — оно принято и остаётся. - Перенос инстанса на другой хост / другой uid (отдельная задача при миграции хоста). - Массовая ретроактивная переработка ADR-001 ORCH-040 (его история не переписывается; допускается forward-breadcrumb-ссылка на ORCH-057 — решает архитектор). - Выбор конкретного варианта реализации one-time нормализации (a/b/в) — зона архитектора (06-adr). ## 3. Заинтересованные стороны - **Заказчик / Owner** — Слава (homenet542), инициатор; принимает результат. - **Эксплуатация** — Стрим (применял ручной workaround); потребитель процедуры в INFRA.md. - **Затронутые проекты** — `orchestrator` (self-hosting) и `enduro-trails` (общий инстанс, общая очередь, общий bind-mount `/repos`): нормализация прав `/repos` касается обоих репо. ## 4. Бизнес-требования (BR) - **BR-1** — После миграции контейнера на новый uid конвейер запускается **без ручного `chown`**: либо авто-нормализация прав, либо **явная блокирующая ошибка с инструкцией** (никогда не сырой `git fatal` на launch). - **BR-2** — На свежей среде / новом репо / после исторического запуска под root проблема **не воспроизводится** (детект + понятная диагностика срабатывают до падения агента). - **BR-3** — `INFRA.md` и ADR содержат **конкретную процедуру** нормализации legacy root-файлов (точные команды, область: `_wt`, `.git/objects`, `.git/worktrees`, `data/runs`), помеченную как обязательный шаг миграции uid. - **BR-4** — Несоответствие владельца наблюдаемо: оператор узнаёт о проблеме из лога/уведомления/ read-only статуса, а не по падению задачи на launch. - **BR-5** — Защита `ensure_worktree` распознаёт класс ошибки «нет прав на создание worktree» и сообщает причину + лечащую команду (опц. — авто-самолечение, если процесс имеет права). ## 5. Нефункциональные требования (NFR) - **NFR-1 (self-hosting безопасность)** — Решение **никогда** не перезапускает/не роняет прод-контейнер `orchestrator`, не трогает `main`/force-push/прод-образ. Контейнер бежит под uid 1000 (без root) → код **не может** делать `chown` без root; код ограничивается детектом + внятной диагностикой/блокировкой, а фактический `chown` — операторская/init-процедура. - **NFR-2 (общий инстанс)** — Нулевая регрессия для `enduro-trails`: feature под kill-switch и scope-флагом (по образцу `serial_gate`/`coverage_gate`); выключено → поведение 1:1 как до ORCH-057. - **NFR-3 (never-raise / fail-safe)** — Детект-леаф никогда не бросает наружу неожиданное исключение и не блокирует старт сервиса по своей ошибке; деградирует в WARNING. - **NFR-4 (идемпотентность)** — Повторный запуск детекта/нормализации на уже корректной среде — no-op без побочных эффектов. - **NFR-5 (обратимость)** — Поведение откатывается выключением kill-switch без миграций/правки схемы. - **NFR-6 (наблюдаемость)** — Вердикт (есть/нет mismatch, сколько файлов, какие корни) логируется структурно; при проблеме — Telegram с кликабельным номером задачи (если применимо) + read-only отражение в `GET /queue`. ## 6. Допущения и ограничения - Целевой uid:gid рантайма = `1000:1000` (slin), подтверждён ORCH-040 (P-3); на хосте `/repos`, `/app/data` штатно `1000:1000`. - Контейнер бежит под numeric uid 1000 без записи в `/etc/passwd` базового образа; в образе создан реальный user `slin` (uid 1000) для `getpwuid()` (ORCH-058, Dockerfile). Под uid 1000 `chown` чужих (root) файлов **невозможен** без CAP_CHOWN/root. - `git config --system --add safe.directory '*'` уже в образе — git доверяет bind-mount. - Корни проверки: `ORCH_REPOS_DIR` (`/repos`), включая `_wt`, `/.git/objects`, `/.git/worktrees`, и `data/runs` (37 root-логов в инциденте). - `start_pipeline` (ORCH-088) отложил срез ветки на момент claim analyst-job → детект уместен и на старте сервиса, и перед claim'ом (точку выбирает архитектор). ## 7. Критерии успеха После миграции uid (или на чистой среде) первый же job проходит launch без ручного `chown`, либо — если права не нормализованы — конвейер выдаёт **понятную блокирующую диагностику** с командой исправления вместо сырого `git fatal`. INFRA.md/ADR содержат воспроизводимую процедуру. Для `enduro-trails` — нулевая регрессия. Детальные PASS/FAIL — в `03-acceptance-criteria.md`. ## 8. Риски - Контейнер без root не может `chown` → авто-самолечение возможно только частично/при наличии прав; основной гарант — детект+диагностика+процедура (детали — `10-tech-risks.md`, архитектор). - Рекурсивный обход больших `.git/objects` / `_wt` может быть дорог → нужен дешёвый/семплированный детект и кэш (как preflight TTL). - Ложно-блокирующая ошибка может застопорить и enduro-trails (общий `/repos`) → строгий scope/fail-safe. - Правка `docker-compose.yml`/entrypoint (init-контейнер) = деплой self → групповой риск (NFR-1), обязательная страховка staging.