From 94e164b379d1741f9eed6cb2d36561ac72acd573 Mon Sep 17 00:00:00 2001 From: Fedorov Vladimir Date: Sat, 3 Oct 2026 01:29:41 +0700 Subject: [PATCH] fix backend --- GO-001-plan.md | 217 ------------------------------------------------- GO-002-plan.md | 164 ------------------------------------- todo.md | 170 -------------------------------------- 3 files changed, 551 deletions(-) delete mode 100644 GO-001-plan.md delete mode 100644 GO-002-plan.md delete mode 100644 todo.md diff --git a/GO-001-plan.md b/GO-001-plan.md deleted file mode 100644 index 0ccfe9d..0000000 --- a/GO-001-plan.md +++ /dev/null @@ -1,217 +0,0 @@ -# План GO-001: убрать прямые зависимости handler-ов от repos и modules - -## Цель - -`internal/app` принимает transport-вход, извлекает пользователя и параметры, -вызывает прикладной service и преобразует результат в protobuf/HTTP. Handler не -импортирует repository и не владеет прикладной операцией. - -Не трогаем в этой задаче: - -- `proto/`, API-контракт и protobuf-генерацию; -- wiring в `cmd`, где repositories и modules должны собираться; -- `roles` и `processor_jwt`, если они используются только для извлечения - identity/проверки transport-доступа; -- единое исправление `err.Error()` во всех handler-ах — это GO-003. - -## Текущие точки нарушения - -| Участок | Прямые зависимости app | Что оставить в app | Что должно уйти в service | -| --- | --- | --- | --- | -| `app/team_registration.go` | `teams_repo` | HTTP/JSON, auth token, timeout | смысл ошибок регистрации | -| `app/team_qr.go` | `games_repo`, `team_qr_pdf` | auth, headers, `HttpBody` | ошибки игры и PDF | -| `app/users.go` | `users_repo` | claims, metadata, transport auth | ошибка отсутствующего пользователя | -| `app/scenario_archive.go` | `scenarios_repo`, archive/string modules | auth, ZIP headers, `HttpBody` | лимиты/ошибки сценарного архива | -| `app/files.go` | `file_storage` | auth, headers, `HttpBody` | ошибка отсутствующего файла | -| mapper-ы app | `game_model`, `formatter_utils` | protobuf mapping | модели и преобразования use-case | - -## Маленькие этапы - -### GO-001.0 — зафиксировать baseline и границы - -1. Проверить текущий список импортов: - - ```sh - rg -n 'internal/(repos|modules)' evening_detective_server/internal/app --glob '*.go' - cd evening_detective_server && go test ./internal/app ./internal/services/... - ``` - -2. Выписать в diff только файлы из таблицы выше. -3. После каждого следующего этапа проверять, что импорт repository исчез только - из app, а wiring в `cmd` остался. - -Готово, если до изменений есть зафиксированный список импортов и понятен -targeted test для каждого участка. - -### GO-001.1 — webhook регистрации команды - -Файлы: `internal/app/team_registration.go`, -`internal/services/team_service/registration.go` и связанные тесты. - -1. Найти, какие sentinel-ошибки `TeamService.RegisterTeam` уже возвращает. -2. Сделать ошибку закрытой регистрации частью контракта `team_service`, чтобы - handler не импортировал `teams_repo` только ради `errors.Is`. -3. Оставить `TeamRegistrationHandler` ответственным только за проверку bearer - token, разбор JSON/JSON-RPC, timeout и HTTP-код. -4. Обновить тесты handler-а на публичную ошибку service; не переносить JSON - parsing в service. - -Проверка этапа: - -```sh -cd evening_detective_server -go test ./internal/app ./internal/services/team_service -! rg 'internal/repos' internal/app/team_registration.go -``` - -Ожидаемый результат: регистрация с закрытым окном по-прежнему даёт `409`, -ошибочный payload — `400`, а app больше не знает `teams_repo`. - -### GO-001.2 — выдача QR-кодов команд - -Файлы: `internal/app/team_qr.go`, -`internal/services/team_service/service.go`, `team_qr.go` и тесты. - -1. Перенести наружный смысл ошибок из repository/PDF-модуля на границу - `team_service`: игра не найдена и у команды нет карточек. -2. Не переносить в service формирование `HttpBody` и HTTP-заголовков. -3. В handler оставить проверку claims/ролей, валидацию `id`, mapping ошибок в - gRPC-коды и headers. -4. Проверить существующие тесты `team_qr_test.go` и добавить только недостающий - regression case на обе ошибки. - -Проверка этапа: - -```sh -cd evening_detective_server -go test ./internal/app ./internal/services/team_service -! rg 'internal/repos|team_qr_pdf' internal/app/team_qr.go -``` - -Ожидаемый результат: `DownloadTeamQR` сохраняет PDF и заголовки без прямой -зависимости app от repository/PDF implementation. - -### GO-001.3 — операции пользователей - -Файлы: `internal/app/users.go`, `internal/services/users_service/*` и тесты. - -1. Найти, почему app использует `users_repo.ErrUserNotFound` только в - `ChangeUserPassword`. -2. Передать наружу ошибку через контракт `users_service` (владелец прикладного - смысла), не раскрывая в app storage-пакет. -3. Не менять здесь многочисленные protobuf-поля `Error`: их единое исправление - относится к GO-003. -4. Оставить в app извлечение claims, пароля из metadata, роли и вызов service. -5. Обновить unit-тесты на `NotFound` через service-owned error. - -Проверка этапа: - -```sh -cd evening_detective_server -go test ./internal/app ./internal/services/users_service -! rg 'internal/repos' internal/app/users.go -``` - -Ожидаемый результат: app не импортирует `users_repo`, а код gRPC для -отсутствующего пользователя не меняется. - -### GO-001.4 — архив сценария - -Файлы: `internal/app/scenario_archive.go`, -`internal/services/scenarios_service/*` и тесты. - -1. Отделить transport-часть от прикладной: app оставляет auth, `HttpBody`, - `Content-Disposition` и проверку размера запроса. -2. Ошибку сценария не найдено получать через `scenarios_repo` в app; использовать - service-owned контракт ошибки. -3. Проверить, нужен ли app доступ к `scenario_archive.MaxArchiveSize()` только - как к transport-лимиту. Если да, предоставить лимит через service-owned - константу/функцию; не тащить repository или storage-модель. -4. `string_tools` оставить только если это чистая transport-санитизация имени; - иначе вернуть готовое безопасное имя из service. Не переносить бизнес-логику - в mapper. -5. Не менять формат ZIP и правила авторизации. - -Проверка этапа: - -```sh -cd evening_detective_server -go test ./internal/app ./internal/services/scenarios_service -! rg 'internal/repos' internal/app/scenario_archive.go -``` - -Ожидаемый результат: archive handler знает HTTP-представление, а не repository -и внутреннюю реализацию архива. - -### GO-001.5 — скачивание файлов - -Файлы: `internal/app/files.go`, `internal/services/file_service/*` и тесты. - -1. Перенести `ErrFileNotFound` на публичную границу `file_service` (или сделать - service-owned alias без нового слоя), чтобы handler не импортировал - `file_storage`. -2. Оставить в app авторизацию автора, `nosniff`, `HttpBody` и mapping MIME-типа. -3. Проверить, что service уже возвращает полную модель файла и контекст; - новых DTO и интерфейсов не добавлять. -4. Добавить/обновить тест на `404` скачивания отсутствующего файла. - -Проверка этапа: - -```sh -cd evening_detective_server -go test ./internal/app ./internal/services/file_service -! rg 'internal/modules/file_storage' internal/app/files.go -``` - -### GO-001.6 — app mapper-ы и чистка импортов - -Файлы: `internal/app/game_mapper.go`, `team_mapper.go`, `story_mapper.go`, -`internal/app/*`. - -1. Не переносить protobuf mapping в service: он остаётся в `app`. -2. Проверить, что входные типы mapper-ов являются контрактом service, а не - типами repository/module. Если mapper использует `game_model` или - `formatter_utils` только как промежуточный use-case тип, перенести границу - в service минимальным изменением, сохранив существующий protobuf output. -3. Не переименовывать пакеты `*_utils` в рамках GO-001 — это GO-006. -4. После каждого переноса удалить неиспользуемые импорты и проверить callers. - -Проверка этапа: - -```sh -cd evening_detective_server -find internal/app internal/services -name '*.go' -print0 | xargs -0 gofmt -l -go vet ./... -go test ./internal/app ./internal/services/... -``` - -### GO-001.7 — финальная проверка архитектурного правила - -1. Runtime-файлы `internal/app` не должны импортировать `internal/repos`. -2. Каждый RPC/HTTP handler должен вызывать service, а не repository/module - с бизнес-операцией. -3. `cmd/evening_detective_server/main.go` продолжает собирать repositories, - modules и services — его зависимости не удалять. -4. Проверить все callers изменённых ошибок и конструкторов. -5. Запустить полный набор проверок: - - ```sh - cd evening_detective_server - find internal/app internal/services -name '*.go' -print0 | xargs -0 gofmt -l - go vet ./... - go test ./... - ``` - -6. В `todo.md` отметить GO-001 выполненной только после успешной проверки - за пределами sandbox, где разрешён loopback TCP. - -## Критерий готовности GO-001 - -- Runtime `internal/app` не импортирует `internal/repos`. -- В app не осталось прямого вызова repository или технического module для - выполнения бизнес-операции. -- Существующие transport-коды, headers, JSON payloads и protobuf responses не - изменились намеренно. -- Для каждого перенесённого error mapping есть targeted test. -- `gofmt`, `go vet ./...` и полный `go test ./...` проходят в поддерживаемом - окружении. diff --git a/GO-002-plan.md b/GO-002-plan.md deleted file mode 100644 index 2375b45..0000000 --- a/GO-002-plan.md +++ /dev/null @@ -1,164 +0,0 @@ -# План GO-002: разорвать зависимости modules от repos и services - -## Цель - -Технические modules не импортируют прикладные `services` и storage `repos`. -Модуль получает собственные входные типы, а преобразование repository-моделей -делается на границе service. - -Не меняем: - -- protobuf/API и сгенерированные файлы; -- формат `scenario.json`, ZIP-архива и публичные ответы; -- бизнес-правила и SQL; -- `internal/modules/storytelling`, пока он не импортирует repos/services; -- GO-006 (`*_utils`) и GO-003 (единый error mapping). - -## Фактические нарушения - -| Module | Нарушение | Callers | -| --- | --- | --- | -| `modules/game_model` | импортирует `repos` и `services/scenarios_service` | game, gameplay, answer, team, MCP, app | -| `modules/scenario_archive` | `Pack` принимает `*repos.Scenario` | `scenarios_service`, module tests | -| `modules/scenario_archive` | импортирует `repos` только для storage-модели | только archive implementation | - -## Правило миграции - -Каждый кусок заканчивается рабочим компилятором и targeted-тестами. После -каждого шага проверять запрещённые импорты: - -```sh -cd evening_detective_server -rg -n '"evening_detective_server/internal/(repos|services)' internal/modules --glob '*.go' --glob '!*_test.go' -go test ./internal/modules/... ./internal/services/... -``` - -## Маленькие этапы - -### GO-002.0 — зафиксировать baseline - -1. Сохранить список runtime-файлов с нарушениями: - `modules/game_model/game.go`, `game_mapper.go`, `team_mapper.go` и - `modules/scenario_archive/interface.go`/`archive.go`. -2. Проверить чистое состояние тестов: - - ```sh - GOCACHE=/tmp/evening-detective-go-cache go test ./internal/modules/... ./internal/services/... - ``` - -3. Не менять callers и не создавать `common`, `domain` или `utils` только для - обхода импортов. - -Готово, если baseline проходит и список зависимостей зафиксирован. - -### GO-002.1 — выделить собственный входной тип archive module - -Файлы: `internal/modules/scenario_archive/interface.go`, `archive.go` и -`archive_test.go`. - -1. Добавить в module небольшой тип входа для упаковки архива, содержащий только - нужные поля: `Name`, `Description`, `Image` и сериализованную `Story`. -2. Изменить `IScenarioArchive.Pack` так, чтобы он принимал этот тип, а не - `*repos.Scenario`. -3. В implementation заменить обращения к `scenario.Scenario`, - `scenario.Description` и другим полям на поля нового типа. -4. Перевести module tests на новый тип. Формат `ScenarioJSON` не менять. - -Проверка: - -```sh -cd evening_detective_server -find internal/modules/scenario_archive -name '*.go' -print0 | xargs -0 gofmt -w -go test ./internal/modules/scenario_archive -! rg 'internal/repos' internal/modules/scenario_archive --glob '*.go' -``` - -Ожидаемый результат: archive module полностью независим от storage package. - -### GO-002.2 — сделать mapping repository → archive в scenarios service - -Файлы: `internal/services/scenarios_service/service.go`, mapper и тесты. - -1. В `scenarios_service` собрать новый archive input из уже полученного - `*repos.Scenario`. -2. Передавать его в `scenarioArchive.Pack`; repository-зависимость остаётся - только в service, где ей место. -3. Не переносить в module права, запросы, storage и protobuf. -4. Сохранить поведение изображений, `nil`-описания и ссылок на story. -5. Добавить/обновить один round-trip тест `DownloadArchive` → `UploadArchive`. - -Проверка: - -```sh -cd evening_detective_server -go test ./internal/services/scenarios_service ./internal/modules/scenario_archive -! rg 'internal/repos' internal/modules/scenario_archive --glob '*.go' -``` - -### GO-002.3 — отделить game model от modules - -Файлы: текущий `internal/modules/game_model/*` и callers. - -1. Переместить типы `Game` и `Team` в пакет с той же предметной ответственностью - под `internal/services/game_model`. -2. Сохранить имена типов и поля, чтобы это была механическая смена import path, - а не новая модель или DTO. -3. Перенести вместе с типами текущие `MapGame`, `MapGames`, `MapTeam`, - `MapTeams`; они являются преобразованием storage-моделей в use-case-модели. -4. Обновить callers в `game_service`, `gameplay_service`, `answer_service`, - `team_service`, `mcp_service`, app и тестах. -5. Не делать пакет `common` и не дублировать структуры. - -Проверка после механического переноса: - -```sh -cd evening_detective_server -find internal/services/game_model internal/services/game_service internal/services/gameplay_service internal/services/answer_service internal/services/team_service internal/services/mcp_service internal/app -name '*.go' -print0 | xargs -0 gofmt -w -go test ./internal/services/game_model ./internal/services/game_service ./internal/services/gameplay_service ./internal/services/answer_service ./internal/services/team_service ./internal/services/mcp_service ./internal/app -! rg 'internal/modules/game_model' --glob '*.go' -``` - -### GO-002.4 — удалить старый module game_model - -1. После успешного переноса убедиться, что `internal/modules/game_model` больше - не импортируется и не нужен через тесты. -2. Удалить только старые файлы package; новые копии не оставлять параллельно. -3. Проверить `go list ./...`, чтобы не осталось пустого/дублирующего пакета. - -Проверка: - -```sh -cd evening_detective_server -go list ./... -go test ./... -! test -d internal/modules/game_model -``` - -### GO-002.5 — финальная проверка направления зависимостей - -1. Runtime modules не импортируют `internal/repos`, `internal/services` или - `internal/app`. -2. `scenario_archive` принимает только собственные типы и callbacks; SQL, - права и storage остаются снаружи. -3. `cmd` по-прежнему собирает concrete repositories/modules/services. -4. Проверить, что сценарный архив и game/team responses побайтно/структурно не - изменились в существующих тестах. -5. Запустить финальный gate: - - ```sh - cd evening_detective_server - find internal -type f -name '*.go' -print0 | xargs -0 gofmt -l - go vet ./... - go test ./... - ``` - -6. Только после успешного полного gate отметить GO-002 как `DONE` в `todo.md`. - -## Критерий готовности GO-002 - -- `rg 'internal/(repos|services)' internal/modules --glob '*.go' --glob '!*_test.go'` не находит runtime-импортов. -- Нет дубликатов `Game`, `Team` или archive input model. -- Все callers используют один пакет `services/game_model`. -- ZIP/`scenario.json`, game/team responses и существующие error semantics не - изменились. -- `gofmt`, `go vet ./...` и `go test ./...` проходят. diff --git a/todo.md b/todo.md deleted file mode 100644 index 045ef2a..0000000 --- a/todo.md +++ /dev/null @@ -1,170 +0,0 @@ -# TODO: привести Go-бэкенд к стандарту - -Аудит проведён по `evening_detective_server` 2026-10-02. - -## Результат проверки - -- `gofmt -l` — нарушений форматирования не найдено. -- `go vet ./...` — замечаний нет. -- `go test ./...` — не проходит в sandbox: тесты gateway и MCP не могут открыть - локальный TCP-порт (`operation not permitted`). Остальные пакеты проходят. -- Сгенерированные `proto/*.pb.go`, Swagger, `.gotmp`, `.gocache` и - `.VSCodeCounter` не входят в ручной аудит. - -## Приоритет P0 — сначала исправить границы приложения - -### GO-001 — убрать прямые зависимости handler-ов от repos и технических modules - -- Статус: `DONE` (этапы GO-001.1–GO-001.7 выполнены) -- Результат: runtime `internal/app` не импортирует `internal/repos`; error - mapping перенесён на границы services; одноиспользуемый `formatter_utils` - удалён. -- Проверка: `gofmt`, `go vet ./...`, `go test ./...` проходят. - -- Стандарт нарушен: `internal/app` должен вызывать прикладные сервисы и делать - только transport mapping. -- Прямые repository-зависимости: `app/scenario_archive.go`, `app/team_qr.go`, - `app/team_registration.go`, `app/users.go`. -- Прямые module-зависимости с прикладной логикой/хранилищем: `app/files.go`, - `app/story_mapper.go`, `app/game_mapper.go`, `app/team_mapper.go`. -- Перенести операции в существующие сервисы, оставив в handler-е извлечение - запроса, вызов одного сервиса и преобразование ответа. -- Проверка: `go vet ./...`, targeted tests `internal/app/...`, затем `go test ./...`. - -### GO-002 — разорвать неправильные зависимости modules от application layers - -- Статус: `DONE` (этапы GO-002.1–GO-002.5 выполнены) -- Результат: archive module использует собственный input type, `game_model` - перенесён в `internal/services/game_model`, старые module-зависимости удалены. -- Проверка: runtime modules не импортируют repos/services/app; `gofmt`, - `go vet ./...` и `go test ./...` проходят. - -- `internal/modules/game_model` импортирует `internal/repos` и - `internal/services/scenarios_service`. -- `internal/modules/scenario_archive` импортирует типы `internal/repos`. -- Это нарушает правило: module не знает конкретный use case, service и storage - models не должны протекать в технический module. -- Оставить доменные типы у владельца; mapper-ы перенести на границы service или - app. Не вводить `common`/`utils` для обхода цикла. -- Проверка: `go list -deps`/ревью импортов и `go test ./...`. - -### GO-003 — привести ошибки handler-ов к единому gRPC/gateway-механизму - -- Во многих handler-ах (`answers.go`, `games.go`, `scenarios.go`, `teams.go`, - `users.go`, `game_reviews.go`, `tickets.go`) внутренняя ошибка превращается в - `err.Error()` внутри успешного protobuf-ответа. -- Это теряет тип ошибки и ведёт к разным правилам HTTP/gRPC mapping; стандарт - требует возвращать ошибку через существующий transport-механизм. -- Ввести/переиспользовать единый mapping sentinel-ошибок в одном месте; не - дублировать проверки по handler-ам и не отдавать внутренние тексты наружу. -- Добавить минимальные тесты на коды `NotFound`, `InvalidArgument`, `Conflict` и - `Internal` для representative endpoint-ов. - -## Приоритет P1 — механические нарушения стандарта - -### GO-004 — отформатировать SQL и вызовы pgx по правилам проекта - -- Нарушены многострочный raw SQL, вертикальные clauses и многострочные вызовы. -- Зафиксированные примеры: `repos/screen_sessions_repo/repo.go` (несколько - `Exec` в одну строку), `repos/teams_repo/repo.go`, - `repos/teams_repo/participation.go`, `repos/tickets_repo/repo.go`, - `repos/users_repo/repo.go`. -- Пройти все runtime-файлы `internal/repos`, не менять SQL-смысл. -- Одновременно проверить для каждого `Query`: `defer rows.Close()` сразу после - успешного запроса и обязательный `rows.Err()`. -- Проверка: `gofmt -w` изменённых файлов, `go test ./internal/repos/...`. - -### GO-005 — исправить имена конструкторов и интерфейсов - -- Generic-конструкторы `New` находятся в `repos/health_repo/repo.go` и - `repos/tickets_repo/repo.go`; переименовать в предметные - `NewHealthRepo`/`NewTicketsRepo` и обновить wiring. -- Интерфейсы без обязательного префикса `I`: `GamePlayer`, `AnswerSubmitter`, - `usersRepository`; проверить также дублирующий `ICleaner` в - `modules/storytelling/dependency.go`. -- Не создавать новые интерфейсы: только переименовать или удалить дубликаты, - если это не ломает границу подмены. -- Проверка: `go test ./...`. - -### GO-006 — убрать generic-пакеты `*_utils` - -- Стандарт запрещает `utils` для несвязанных функций; найдены - `modules/formatter_utils` и `modules/network_utils`. -- Переименовать их по ответственности и обновить импорты: например, пакет - форматирования результата и пакет определения локальных адресов. -- Не переносить код и не добавлять общий helper-пакет без конкретного владельца. -- Проверка: поиск `utils|helpers|common|misc` среди пакетов и `go test ./...`. - -### GO-007 — привести комментарии исходников к русскому стандарту - -- Найдены английские комментарии в runtime/test-коде, например - `modules/processor_jwt/context.go` и комментарий в - `services/gameplay_service/finished_story_test.go`. -- Перевести только комментарии проекта; директивы `go:embed` и сгенерированный - код не трогать. -- Заодно документировать публичные символы, у которых комментарий отсутствует - и назначение неочевидно. -- Проверка: ручной проход `go doc`-символов и `go vet ./...`. - -## Приоритет P1 — привести тесты к зафиксированному стандарту - -### GO-008 — убрать прямую подготовку и проверку SQL из тестовых сценариев - -- `repos/tickets_repo/accept_test.go` и `repos/teams_repo/registration_test.go` - напрямую вызывают `Exec`/`QueryRow` и создают схему в тесте. -- Подготовку выполнять миграциями инфраструктурного слоя и публичными методами - repository/service; проверки поведения делать через API тестируемого слоя. -- Сохранить отдельную БД через `TEST_DATABASE_URL`; не подключаться к production. -- Проверка: targeted integration tests с отдельной PostgreSQL-базой. - -### GO-009 — стабилизировать сетевые тесты и общий тестовый gate - -- `cmd/evening_detective_server/gateway_test.go` и - `services/mcp_service/http_test.go` требуют loopback TCP; в текущем окружении - они завершаются `operation not permitted`. -- Сделать сетевой слой тестов запускаемым в поддерживаемом CI-окружении либо - отделить такие тесты явным integration/build tag; не ослаблять проверки - поведения и не маскировать ошибки `t.Skip` без условия окружения. -- После этого зафиксировать обязательный зелёный gate `go test ./...`. - -### GO-010 — выровнять структуру unit-тестов - -- Проверить `*_test.go` на правило: тесты рядом с владельцем поведения, - бизнес-правила — без PostgreSQL, новая ветка — с минимальным регрессионным - тестом, кейсы — table-driven. -- В первую очередь пройти тесты с прямым `panic`, сравнением текста ошибки и - неописательными сценариями; например `users_service/service_test.go`, - `users_service/change_password_test.go`, `scenarios_service/service_test.go`. -- Не переписывать существующие тесты механически, если это не повышает - проверяемость конкретного правила. - -## Приоритет P2 — завершить миграцию без изменения поведения - -### GO-011 — инвентаризировать mapping и модели по владельцам - -- Часть mapper-ов находится в `app`, часть в service, часть в modules; есть - mapping через repository-типы и потенциальные дубли моделей. -- Для каждого mapping выбрать единственного владельца по контракту стандарта: - transport — `app`, use-case/storage — соответствующий service/repo. -- Литералы mapping оставить вертикальными; I/O и бизнес-решения из mapper-ов - убрать. -- Проверка: review зависимостей и targeted tests mapper-ов. - -### GO-012 — ввести лёгкий автоматический guard стандарта - -- После исправления кода добавить только минимальные проверки без новой - зависимости: `gofmt -l`, `go vet ./...`, проверка запрещённых импортов - `app → repos`, `services → proto`, `repos → proto`, а также `go test ./...`. -- Разместить команду в существующем `Makefile`, если она не дублирует уже - имеющуюся проверку. -- Не подключать golangci-lint/staticcheck только ради этого списка. - -## Порядок выполнения сегодня - -1. `GO-001` → `GO-002` → `GO-003`. -2. `GO-004` → `GO-005` → `GO-006` → `GO-007`. -3. `GO-008` → `GO-009` → `GO-010`. -4. `GO-011` → `GO-012`. - -Сгенерированные protobuf-файлы не форматировать вручную. Коммиты и push не -выполнять.