## 1. Ревью дизайна (чекпоинт ДО кода)

- [x] 1.1 Согласовать design.md: стратегию миграции/backfill, снятие `DEFAULT`,
  секцию `[general]` с `timezone`. Правки внести до реализации.

## 2. Хелперы формата времени (store)

- [x] 2.1 `internal/store/download.go`: убрать `sqliteTimeLayout`; `FormatTime` →
  `t.UTC().Format(time.RFC3339)`, `ParseTime` → `time.Parse(time.RFC3339, s)`.
  Обновить комментарии (формат `...Z`, не `datetime('now')`), включая коммент про
  `SourceAddedAt` на `download.go:118` (ссылается на `sqliteTimeLayout`).
- [x] 2.2 Добавить `store.Now()` (обёртка над `time.Now().UTC()`) как единую
  точку получения «сейчас» в слое store.
- [x] 2.3 Проверить `migrations/0006_ulid_identity.go:204` (`parseCreatedAt`
  парсит старый формат) — это историческая миграция, формат менять не нужно;
  убедиться, что она не ломается и не пересекается с новой.

## 3. Убрать DEFAULT и писать время из Go

- [x] 3.1 Каждый INSERT-**сайт** со временем явно передаёт `created_at`/`updated_at`
  (= `FormatTime(store.Now())`). Полный список сайтов (не методов!):
  `download.go` — `CreateDownloadIfNoActive` (вставка download :219 + вставка
  хешей :228), dedup top-up хешей :203 (`INSERT OR IGNORE`), `AddInfohashes` :306
  (`INSERT OR IGNORE`); `recognition.go` — `CreateRecognition` :72, `AddHint` :108,
  `SetOverride` :130, `AddFileLink` :185, `AddMetadataCandidate` :332. Для
  `INSERT OR IGNORE` в `download_infohash` `created_at` обязателен на новой строке
  (после снятия `DEFAULT` + `NOT NULL`); при конфликте PK строка не вставляется — ок.
- [x] 3.2 `download.go:510`: инлайн `updated_at = datetime('now')` → параметр
  `FormatTime(store.Now())` (sortability-ловушка — проверить, что не осталось
  сырых `datetime('now')` в SQL: `grep -rn "datetime('now')" internal`).

## 4. Миграция БД (goose)

- [x] 4.1 Новая миграция (стиль `0006`): backfill каждой timestamp-колонки
  `strftime('%Y-%m-%dT%H:%M:%SZ', col)` (7 таблиц: download.created_at/updated_at/
  source_added_at, download_infohash/recognition/hint/override/metadata_candidate/
  file_link .created_at) + rebuild таблиц без `DEFAULT` на timestamp-колонках.
- [x] 4.2 Down-миграция: обратный backfill `strftime('%Y-%m-%d %H:%M:%S', col)` +
  восстановление `DEFAULT`.
- [x] 4.3 Тест миграции: строки со старым форматом → новый формат; хронология
  сортировки сохранена; `source_added_at IS NULL` не затронут; обратный backfill
  (down) на строке с `T`/`Z` даёт старый формат.
- [x] 4.4 Обновить существующий `TestUlidMigration` (`migration_test.go:100-101`):
  ассерт ждёт старый формат `2026-01-01 10:00:00`, после новой миграции метки
  станут `...T...Z` — поправить ожидаемое (заодно канарейка, что backfill отработал).

## 5. Настройка таймзоны отображения

- [x] 5.1 `internal/config`: секция `[general]` (`General struct`) с полем
  `Timezone string` (дефолт `UTC`) + распарсенный `*time.Location`; жёсткая
  валидация на старте через `time.LoadLocation`, невалидная зона → ошибка
  конфигурации (без рантайм-фолбэка).
- [x] 5.1a Встроить zoneinfo: `import _ "time/tzdata"` в точке входа
  (`cmd/jellybit`). Тогда `LoadLocation` падает только на битом имени.
- [x] 5.2 `internal/httpapi`: пробросить `*time.Location` до `fmtDate`/`humanizeAge`
  (свободные функции — тянуть параметром через `toView`, вызовы `httpapi.go:288,545`,
  либо сделать методами `server`); заменить зашитый `mskLoc` (`live.go:189-201`)
  целиком (после встраивания tzdata рантайм-фолбэк на UTC внутри `mskLoc` не нужен).
  Guard: `t.In(nil)` паникует — при незаданной зоне подставлять `time.UTC`; тест-хелпер
  `testRouterLive` тоже задаёт loc.
- [x] 5.3 Обновить пример конфига (`config.example.toml`/деплой): секция
  `[general]` первой, `timezone`; для этого сервера — `Europe/Moscow`.

## 6. Документация

- [x] 6.1 `docs/conventions/database.md:42-43`: формат меток → RFC 3339 (UTC,
  `Z`), генерация только из Go (`store.Now`/`FormatTime`), без `DEFAULT`.
- [x] 6.2 `docs/conventions/config.md`: зона отображения конфигурируема (дефолт
  UTC); хранение и логи — UTC.
- [x] 6.3 `docs/specs/database.md`: комментарий формата timestamp-колонок в ER.

## 7. Проверка и ревью кода (чекпоинт до archive)

- [x] 7.1 `task test` / `task lint` зелёные; тест сортировки списка на смешанных
  и однотипных метках.
- [x] 7.2 Ревью кода реализации; затем `openspec validate --strict` и archive.
