chore: добавить AGENTS.md с инструкциями для агентов

Правила работы в репозитории: команды, архитектура, миграции, конфигурация,
фронтенд, OpenSpec и конвенции коммитов. Отдельный раздел про прогоны с
эмулятором: instrumented-тесты и запуск AVD могут зависнуть, прогресс нужно
проверять примерно раз в минуту, а не ждать молча.
This commit is contained in:
iu
2026-10-16 13:28:03 +03:00
parent e4fc6f0ce9
commit 863038b5a0
+126
View File
@@ -0,0 +1,126 @@
# AGENTS.md
Инструкции для агентов, работающих в этом репозитории. Читай вместе с `README.md`
(актуальный источник правды) и `project.md` (исходное ТЗ).
## Что это
Self-hosted фотоальбом: Go-бэкенд с вшитой через `go:embed` статикой SvelteKit,
один статический бинарник (`CGO_ENABLED=0`), без Docker. Плюс отдельный Android-клиент.
По умолчанию (`DB_BACKEND=sqlite`, `STORAGE_BACKEND=fs`) работает полностью локально,
без Postgres и S3. Postgres/Garage (`pg.localnet`, `s3.localnet`) — опциональные слои.
## Команды
Все команды — из корня репозитория. Голый `make` только печатает список целей.
```bash
make build # frontend -> backend/static -> go:embed -> bin/galo
make frontend # npm run build (инкрементально), копирует статику в backend/static
make backend # CGO_ENABLED=0 go build -tags nodynamic -o ../bin/galo
make run # frontend + backend + запуск ./bin/galo
make migrate # применяет только Postgres-миграции (cmd/migrate)
make test # cd backend && go test ./... (только бэкенд!)
make testdata # наполняет запущенный инстанс через scripts/seed.py
make android # cd android && ./gradlew :app:assembleDebug
make install-android-app # сборка + установка debug-APK на устройство
```
- `-tags nodynamic` обязателен: без него `gen2brain/webp` тянет динамический libwebp
и бинарник перестаёт быть статическим.
- Порядок сборки важен: `frontend` должен отработать до `backend`, иначе `go:embed
all:static` вшивает только `backend/static/.gitkeep` и бинарник выходит без UI.
- `make test` не запускает фронтенд-тесты; отдельных make-целей для них нет:
`cd frontend && npm run test` (vitest, окружение `node`, файлы `src/**/*.test.ts`) и
`cd frontend && npm run check` (svelte-check/typecheck).
- Один Go-тест: `cd backend && go test ./internal/photos/ -run TestName -v`.
- Интеграционные тесты Postgres-репозиториев пропускаются без `TEST_DATABASE_URL`:
`TEST_DATABASE_URL='postgres://user:pass@pg.localnet:5432/galo?sslmode=disable' \
cd backend && go test ./internal/repo/ -v`
- Тесты видео (`internal/e2e`) сами пропускаются, если нет `ffmpeg`/`ffprobe`.
Тесты e2e репозиториев поднимают SQLite в `t.TempDir()` и внешних сервисов не требуют.
## Тесты на эмуляторе
Instrumented-прогоны (`:app:connectedDebugAndroidTest`) и сам запуск AVD **могут зависнуть** —
не дожидайся их молча. Пока идёт прогон, проверяй прогресс **примерно раз в минуту**: жив ли
процесс, обновляются ли результаты, отвечает ли устройство:
```bash
pgrep -fa "GradleWrapperMain|Gradle Test Executor"
ls -la android/app/build/outputs/androidTest-results/connected/debug/
adb devices
```
Если прогресса нет дольше пары минут — снимай прогон (`kill`), а не жди дальше, и разбирайся
по дампу потоков (`jstack <pid>` JVM тестов). То же относится к обычным unit-тестам: они тоже
идут в отдельной JVM, и её зависание видно только по тому, что результаты перестали
обновляться.
## Архитектура
- Go-модуль `galo` (`backend/go.mod`, Go 1.26). Точки входа: `backend/cmd/server`,
`cmd/migrate`, `cmd/reconcile`, `cmd/backfill-exif`.
- Доменные пакеты в `backend/internal/<domain>` (albums, auth, members, photos, social,
notifications, sharelinks, storage, sse, settings, video). Каждый содержит сервис,
HTTP-обработчики и `Repository`-интерфейс.
- `internal/store/store.go` — единственное место, которое знает конкретные реализации.
При добавлении метода в `Repository` нужно реализовать его в **обоих** вариантах:
Postgres (`NewStore/NewPostgres`) и SQLite (`NewSQLiteStore/NewSQLite`).
- `backend/internal/app` — маршруты/статика; `internal/repo/contract` — контрактные тесты,
которые гоняют одинаковый набор проверок для SQLite и Postgres.
- `backend/embed.go` вшивает `static` (фронтенд) и `migrations/**`. Паттерны эмбеда —
`migrations/*.sql migrations/sqlite/*.sql`: новые файлы должны им соответствовать.
- Права проверяются на уровне сервиса, не только в UI. Блобы наружу отдаются только
через бэкенд: `GET /api/objects/<key>` с HMAC-подписью, один маршрут для авторизованных
и для share-ссылок. Ключи объектов непрозрачны: `objects/{object_id}/{role}`.
- SSE (`/api/sse/...`) авторизуется через `?token=`, т.к. `EventSource` не шлёт заголовки.
Для nginx критичен `proxy_buffering off`.
- `android/` — самостоятельный Gradle-модуль, серверный и веб-код не трогает; использует
уже существующий HTTP API. Тесты: `cd android && ./gradlew test`.
## Миграции (частый источник ошибок)
Схема поддерживается в **двух диалектах** с независимой нумерацией:
- Postgres: `backend/migrations/NNNNNN_*.{up,down}.sql`
- SQLite: `backend/migrations/sqlite/NNNNNN_*.{up,down}.sql`
Любое изменение схемы нужно добавить в оба каталога, номера версий не обязаны совпадать
(сейчас они разошлись). Миграции вшиты в бинарник и применяются автоматически при старте
сервера для выбранного бэкенда. `make migrate` применяет только Postgres.
`cmd/reconcile` существует для сверки хранилища с журналом `objects` (сборка мусора
журналом управляется и хранилище не сканирует).
## Конфигурация и данные
- `.env` читается вручную в `cmd/*/main.go` из `.env`, затем `../.env` (без dotenv-библиотеки).
- Секреты подписи JWT и URL объектов не задаются в env: генерируются при первом старте и
лежат в слое метаданных (`app_settings`), поэтому переживают перезапуск и рестарт SQLite.
- Локальные артефакты в корне (`galo.db*`, `/data/`, `bin/`, `backend/static/*`,
`frontend/build/`) в git не попадают — не коммить их.
## Фронтенд
- SvelteKit 2 + Svelte 5 (runes), `adapter-static` с `fallback: index.html`, Tailwind v4.
- Тёмная/светлая/нейтральная темы через `data-theme` на `<html>` и семантические
CSS-токены. В компонентах — только семантические классы (`bg-bg-elevated`, `text-muted`
и т.п.), **никаких `dark:` префиксов**.
- Vite dev-сервер проксирует `/api` на `localhost:8080` (см. `vite.config.ts`).
## OpenSpec
Спецификации — в `openspec/specs/<capability>/spec.md`; завершённые изменения —
в `openspec/changes/archive/<YYYY-MM-DD-name>`. Активных изменений сейчас нет.
Workflow-команды и скиллы лежат в `.opencode/` и требуют CLI `openspec`
(в этом окружении он может быть не установлен — проверь `command -v openspec`).
При изменении поведения сначала обновляй/создавай спеку, а не только код.
## Конвенции репозитория
- Ветка `develop`; коммиты — Conventional Commits с русским описанием
(`feat(realtime): ...`, `test(android): ...`, `chore: ...`). Функциональные ветки
вливаются в `develop` через merge-коммиты.
- `project.md` — исходное ТЗ и местами устарел (например, называет S3 бэкендом по
умолчанию и старую раскладку ключей `albums/...`). Ориентируйся на `README.md` и код.