141 lines
12 KiB
Markdown
141 lines
12 KiB
Markdown
---
|
||
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 != <target_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`, `<repo>/.git/objects`,
|
||
`<repo>/.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.
|