11 KiB
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-файлам. Три слоя:
- Защита launcher —
ensure_worktreeраспознаётPermission denied/git-fatal на создании worktree и поднимает внятную ошибку с диагнозом «legacy root-файлы в/repos/_wt— нужна нормализация прав» + лечащая команда (опц. авто-самолечение при наличии прав). - Ранний детект — новый чистый леаф находит файлы с
uid != target_uidвORCH_REPOS_DIR(_wt,.git/objects,.git/worktrees,data/runs); вызывается на старте сервиса и/или перед claim'ом job; never-raise, config-gated, с наблюдаемостью. - Процедура —
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 /queueread-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.