# 🧹 Поднимать сервис и заглушку одной командой - **Тип:** chore - **Категория:** Очередь — оснастка локального прогона: без неё каждую проверку начинают с двух терминалов и ручной уборки - **Зачем:** Локальная проверка требует двух терминалов, ручной остановки обоих процессов и чистки каталога данных; учётную запись заводят проходом по браузеру, а владельца панели — по одноразовой ссылке из журнала. Шаг `task dev CONFIG=<путь> USER=<почта>` поднимает сервис и подставной провайдер OIDC разом, кладёт данные во временный каталог, заводит учётные записи и гасит оба процесса по Ctrl+C. Работа состоит из двух частей, и они едут одним коммитом: инструмент `cmd/devadmin` и шаг `dev`, который его зовёт. Порознь они не нужны — шаг без инструмента не заведёт владельца панели, инструмент без шага останется вызовом, который никто не делает. Задуманное устройство: - конфиг пишет человек и передаёт путём — шаг его не сочиняет; - шаг подменяет каталог данных: кладёт копию переданного конфига во временный каталог со своим `data_dir`, чтобы прогон начинался с пустой базы независимо от того, что написано в конфиге. Временное живёт в `/tmp` — в `data/` писать запрещено (CLAUDE.md, «Запреты»); - владельца панели заводит `cmd/devadmin` — отдельный main-пакет рядом с `cmd/oidcstub`, в образ он не едет. Базу открывает тем же `pbrepo.New`, что и сервис, и потому получает её со схемой целиком; зовётся до подъёма сервиса, на пустом каталоге. Пароль существующего владельца не переписывает; - шаг заводит обычную учётную запись входом: поднимает `cmd/oidcstub` с `-email` из `USER`, сам проходит цепочку входа и тем создаёт запись; - оба процесса идут одной группой, `trap` на `INT` и `TERM` гасит их разом, `wait` держит терминал. ## Затрагивает - `cmd/devadmin` — новый main-пакет: заводит владельца панели в базе; - `Taskfile.yml` — шаг `dev` с переменными `CONFIG` и `USER`; - скрипт запуска в `scripts/` — он попадает под шаг `shell` гейта; - `CLAUDE.md`, раздел «Команды»; - `README.md`, раздел про локальный запуск; - `Dockerfile` — строка сборки называет точку входа поимённо, и новый пакет в образ попасть не должен. ## Критерии приёмки - Команда поднимает оба процесса и печатает адрес, по которому открывать приложение. Оракул: прогон с тестовым конфигом — сервис и заглушка слушают свои порты. - Ctrl+C гасит обоих и не оставляет висящих процессов. Оракул: `pgrep` по обоим именам после остановки — пусто. - После запуска в базе есть учётная запись с переданной почтой, а владелец панели входит своим паролем. Оракул: запрос к коллекции пользователей во временной базе и вход по `auth-with-password` в коллекцию владельцев. - Каталог данных лежит во временном месте, а `data/` не тронут. Оракул: путь базы из журнала подъёма и `git status` после прогона. - `task gate` остаётся зелёным, скрипт проходит `shellcheck`, а образ не получает нового пакета. Оракул: прогон гейта целиком и `task image`. ## Рамки Шаг локальный: наружу не ходит, боевых ключей не требует и в образ не попадает. Пароль владельца в конфиг **не заводится** — требование спеки `storage` «Пароль владельца от панели не лежит в конфигурации» остаётся в силе, и работа его не трогает. Распознавание при выдуманных ключах не работает — это остаётся как есть, шаг его не подменяет.