feat: разбор и хранение тренировок и состояния разума

- секции `workouts` и `stateOfMind` покрыты разбором: тренировка лежит одной
  строкой вместе с маршрутом и внутренними рядами, запись — по ключу `род + id`;
  миграция 00007 заводит обе таблицы и возвращает в очередь `partial`-доставки
  с этими ключами
- сущность заменяется целиком, но условно: приехавшая побеждает, если не теряет
  содержания сохранённой (множество ключей и длины верхнеуровневых массивов), а
  при равном содержании выигрывает версия из более поздней доставки ЖУРНАЛА —
  «побеждает приехавшая» было бы функцией порядка свёртки, и живая витрина
  расходилась бы с пересборкой молча
- отпечаток витрины покрывает тренировки и записи и снимается одним снимком
  базы; отчёт `reindex` считает «было и стало» по каждой единице хранения
This commit is contained in:
av
2026-08-02 13:05:16 +03:00
parent c28de9796e
commit f8200f7f80
47 changed files with 5817 additions and 301 deletions
+17 -2
View File
@@ -75,7 +75,12 @@ type Report struct {
// исходов разошлись бы при первой же правке классификации.
Outcome
Buckets int64
Buckets int64
// Workouts и Records — остальные единицы хранения витрины. Считаются рядом
// с объектами потому, что отпечаток отвечает «да/нет» за витрину целиком, а
// решение о подмене базы необратимо и требует направления расхождения.
Workouts int64
Records int64
Fingerprint string
// Canceled — проигрывание прервано отменой, а не дошло до конца.
@@ -171,6 +176,14 @@ func Run(ctx context.Context, o Options) (Report, error) {
if err != nil {
return stopOr(rep, err)
}
rep.Workouts, err = o.Target.CountWorkouts(ctx)
if err != nil {
return stopOr(rep, err)
}
rep.Records, err = o.Target.CountRecords(ctx)
if err != nil {
return stopOr(rep, err)
}
rep.Fingerprint, err = o.Target.Fingerprint(ctx)
if err != nil {
return stopOr(rep, err)
@@ -190,7 +203,9 @@ func Run(ctx context.Context, o Options) (Report, error) {
"failed_other", rep.FailedOther,
"partial", rep.Partial,
"incomparable", rep.Incomparable,
"buckets", rep.Buckets)
"buckets", rep.Buckets,
"workouts", rep.Workouts,
"records", rep.Records)
return rep, nil
}