tasks: заведены задачи про владельца панели из конфига и команду локального запуска

- admin-owner-from-config первой строкой: сервис заводит владельца панели
  по ключу конфига; задача отменяет требование спеки storage, и это названо
  в рамках
- dev-run-task следом: task dev поднимает сервис и заглушку разом, гасит
  оба по Ctrl+C; зависит от ключа конфига
This commit is contained in:
av
2026-08-15 20:39:45 +03:00
parent daa4c3b6e4
commit 97c6c7c440
3 changed files with 112 additions and 0 deletions
+2
View File
@@ -43,6 +43,8 @@
## Очередь
- [✨ Заводить владельца панели по ключу конфига](items/admin-owner-from-config.md) — Владельца панели заводят по одноразовой ссылке из журнала при первом подъёме, и пароль после этого не хранится нигде: на каждой чистой базе шаг повторяется руками — и локально, где база пересоздаётся каждый прогон, и на выкладке с чистого листа.
- [🧹 Поднимать сервис и заглушку одной командой](items/dev-run-task.md) — Локальная проверка требует двух терминалов, ручной остановки обоих процессов и чистки каталога данных; учётную запись заводят проходом по браузеру.
- [✨ Сделать экран загрузки записи и её состояния](items/upload-and-status-screen.md) — Первое, ради чего приложение открывают: отдать файл и увидеть, что с ним происходит.
- [✨ Сделать экран списка своих записей и чтения текста](items/records-list-screen.md) — Расшифровка сегодня доходит одним сообщением и теряется в переписке; вернуться к ней через неделю нечем.
- [✨ Дать владельцу править запись, возвращать её в работу и видеть её путь](items/audiorecord-actions.md) — С записью нельзя сделать ничего: заголовок ставит одна языковая модель, остановленную возвращает в работу только владелец сервиса в панели, а журнал событий пишется и не читается никем, кроме него же
+59
View File
@@ -0,0 +1,59 @@
# ✨ Заводить владельца панели по ключу конфига
- **Тип:** feature
- **Категория:** Очередь — решение владельца 2026-08-15: ключ конфига нужен и локальному прогону, и выкладке
- **Зачем:** Владельца панели заводят по одноразовой ссылке из журнала при первом подъёме, и пароль после этого не хранится нигде: на каждой чистой базе шаг повторяется руками — и локально, где база пересоздаётся каждый прогон, и на выкладке с чистого листа.
Сервис на подъёме заводит владельца панели администратора по ключу конфига,
когда такой учётной записи ещё нет.
Сторонней командой этого сегодня не сделать: точка входа разбирает свои флаги и
сразу грузит конфиг, поэтому команды хранилища до неё не доходят — проверено
2026-08-15, `go run ./cmd/transcriber superuser upsert …` падает на разборе
конфига. Отсюда и ключ: другого места, откуда взять пароль, у сервиса нет.
## Затрагивает
- секция конфига и её проверка на старте — новый ключ, `internal/config`;
- приведение настроек хранилища при подъёме —
`internal/adapter/repo/pocketbase/provider.go`, там же, где применяются
настройки провайдера OIDC;
- `config.example.toml` — образец ключа;
- `docs/conventions/config.md` — конвенция конфига;
- `docs/security.md` — пароль владельца панели становится содержимым конфига, а
вход в панель заводится сам;
- `openspec/specs/storage/spec.md`, требование «Пароль владельца от панели не
лежит в конфигурации» — задача его **отменяет**. Там же сценарий «Владелец
пароля ещё не задан» с проверкой «ни один ключ конфигурации не несёт пароля от
панели».
## Критерии приёмки
- Подъём на пустой базе с заполненным ключом заводит владельца, и он входит в
панель этим паролем. Оракул: подъём на временном каталоге данных, затем вход по
`/_/` — и запрос к собственному адресу хранилища с полученной сессией.
- Подъём с пустым ключом никого не заводит и старт не роняет. Оракул: подъём на
пустой базе с пустым значением — сервис слушает порт, в коллекции владельцев
записей нет.
- Повторный подъём с уже заведённым владельцем пароль не переписывает. Оракул:
сменить пароль в панели, поднять сервис заново, войти новым паролем — вход
проходит.
- Пароль не встречается ни в журнале, ни в тексте ошибки. Оракул: поиск значения
по выводу подъёма, включая подъём с негодным значением ключа.
## Рамки
Задача идёт **против записанного требования**, и это её главное условие. Спека
`storage` сегодня запрещает ровно то, что здесь заводится, и довод у неё свой:
пароль от панели открывает все записи и все файлы разом, а секрет, лежащий в
конфиге, уезжает вместе с ней в выкладку. Отменять требование — решение владельца
от 2026-08-15; принято оно с открытыми глазами, потому что своей команды завести
владельца у сервиса нет. Работа обязана снять требование дельта-спекой, а не
обойти его молча.
Имя ключа конфига проект числит **необратимым** (CLAUDE.md, «Работа»): раз
заведённый, он поедет в выкладку и в роль Ansible из `pet-project-server`.
Инвариант «Секрет не покидает конфиг» распространяется на этот пароль целиком.
Пустое значение обязано значить «никого не заводить»: иначе выкладка без ключа
перестанет подниматься. Переписывать пароль существующего владельца нельзя —
человек мог сменить его в панели.
+51
View File
@@ -0,0 +1,51 @@
# 🧹 Поднимать сервис и заглушку одной командой
- **Тип:** chore
- **Категория:** Очередь — шаг dev заводит владельца панели тем самым ключом — до него команду не собрать
- **Зачем:** Локальная проверка требует двух терминалов, ручной остановки обоих процессов и чистки каталога данных; учётную запись заводят проходом по браузеру.
Шаг `task dev CONFIG=<путь> USER=<почта>` поднимает сервис и подставной
провайдер OIDC разом, кладёт данные во временный каталог, заводит учётные записи
и гасит оба процесса по Ctrl+C.
Задуманное устройство:
- конфиг пишет человек и передаёт путём — шаг его не сочиняет;
- шаг подменяет каталог данных: кладёт копию переданного конфига во временный
каталог со своим `data_dir`, чтобы прогон начинался с пустой базы независимо от
того, что написано в конфиге. Временное живёт в `/tmp` — в `data/` писать
запрещено (CLAUDE.md, «Запреты»);
- оба процесса идут одной группой, `trap` на `INT` и `TERM` гасит их разом,
`wait` держит терминал;
- шаг заводит обычную учётную запись входом: поднимает `cmd/oidcstub` с
`-email` из `USER`, сам проходит цепочку входа и тем создаёт запись;
- владельца панели заводит **ключ конфига**, который приносит
[admin-owner-from-config](admin-owner-from-config.md). До неё шаг владельца
панели завести не может — отсюда и место в списке.
## Затрагивает
- `Taskfile.yml` — шаг `dev` с переменными `CONFIG` и `USER`;
- скрипт запуска в `scripts/` — он попадает под шаг `shell` гейта;
- `CLAUDE.md`, раздел «Команды»;
- `README.md`, раздел про локальный запуск.
## Критерии приёмки
- Команда поднимает оба процесса и печатает адрес, по которому открывать
приложение. Оракул: прогон с тестовым конфигом — сервис и заглушка слушают свои
порты.
- Ctrl+C гасит обоих и не оставляет висящих процессов. Оракул: `pgrep` по обоим
именам после остановки — пусто.
- После запуска в базе есть учётная запись с переданной почтой. Оракул: запрос к
коллекции пользователей во временной базе.
- Каталог данных лежит во временном месте, а `data/` не тронут. Оракул: путь базы
из журнала подъёма и `git status` после прогона.
- `task gate` остаётся зелёным, скрипт проходит `shellcheck`. Оракул: прогон
гейта целиком.
## Рамки
Шаг локальный: наружу не ходит, боевых ключей не требует и в образ не попадает.
Распознавание при выдуманных ключах не работает — это остаётся как есть, шаг его
не подменяет.