# Execution checkpoints проекта

## Паспорт документа

- Статус документа: living document
- Актуально на: 2 апреля 2026 года
- Владелец: backend/platform-команда
- Пересмотр: при изменении статуса этапов, завершении change package или пересборке delivery-плана
- Область применения: операционный контроль поэтапной реализации проекта с фиксацией факта исполнения
- Связанные документы:
  - [Roadmap](./roadmap.md)
  - [Project change log](./project-change-log.md)
  - [Change package standard](../governance/change-package-standard.md)

## Цель документа

Roadmap отвечает на вопрос “куда идём”. Этот документ отвечает на вопрос “что уже закрыто по факту, что в работе и что ещё не начато”.

Каждый checkpoint должен обновляться только по факту исполнения, а не по обещанию.

Этот документ в детальном виде покрывает активные и ближайшие execution-пакеты фаз `A` и `B`, а также delivery maturity. Фазы `C`, `D` и `E` из roadmap пока остаются стратегическими и не дробятся на checkpoint'ы до закрытия MVP hardening, чтобы не создавать ложную детализацию.

## Статусы

- `[x] completed` — выполнено и подтверждено кодом, runtime или документально
- `[~] in progress` — активная работа идёт, но exit criteria ещё не закрыты
- `[ ] planned` — запланировано, но не начато
- `[!] blocked` — работа упёрлась во внешний или внутренний blocker

## 1. Foundation и transferability

### CP-001 — Split-репозитории и production cutover

- Статус: `[x] completed`
- Суть: `qadam-core` и `qadam-web` выделены, production переведён на них
- Факт исполнения:
  - runtime работает из `/data/qadam-core` и `/data/qadam-web`
  - legacy `/data/uzbek` больше не production source of truth
- Доказательства:
  - [Текущее состояние](./current-state.md)
  - [Runbook эксплуатации и деплоя](../operations/deployment-runbook.md)

### CP-002 — Roadmap вынесен в отдельный сервис

- Статус: `[x] completed`
- Суть: `qadam-roadmap` отделён от product web
- Факт исполнения:
  - отдельный `systemd` unit `qadam-roadmap`
  - отдельный runtime `qadam-web/apps/roadmap`
  - `https://qadam.2fab.app/roadmap` возвращает `404`
- Доказательства:
  - [Текущее состояние](./current-state.md)
  - [Runbook эксплуатации и деплоя](../operations/deployment-runbook.md)

### CP-003 — Канонический docs-layer и стандарт документации

- Статус: `[x] completed`
- Суть: документация разложена по разделам, введён `Паспорт документа`, работает перелинковка
- Факт исполнения:
  - канонический слой живёт в `qadam-core/docs`
  - roadmap-портал читает живые markdown-файлы
  - действует [Стандарт документации](../governance/documentation-standard.md)
- Доказательства:
  - [README документации](../README.md)
  - [Стандарт документации](../governance/documentation-standard.md)

### CP-004 — Ops/governance baseline для handover

- Статус: `[x] completed`
- Суть: закрыты базовые runbook и governance-пробелы по эксплуатации, ownership и change package
- Факт исполнения:
  - добавлены backup/restore, incident response, environment matrix, post-deploy checklist
  - зафиксированы ownership model и change package standard
- Доказательства:
  - [Backup/restore runbook](../operations/backup-restore-runbook.md)
  - [Incident response](../operations/incident-response.md)
  - [Environment matrix](../operations/environment-matrix.md)
  - [Ownership model](../governance/ownership-model.md)

### CP-005 — Docs quality gate

- Статус: `[x] completed`
- Суть: у документационного слоя есть исполнимая машинная проверка
- Факт исполнения:
  - в `qadam-core` существует команда `pnpm check:docs`
  - она проверяет паспорт документа и относительные markdown-ссылки
- Доказательства:
  - `package.json` и `scripts/check-docs.mjs` в `qadam-core`

### CP-006 — Backend test и quality gate stabilization

- Статус: `[x] completed`
- Суть: довести backend test contour и merge gates до предсказуемо зелёного состояния после auth/registration/openapi drift
- Факт исполнения:
  - `pnpm -C /data/qadam-core test` проходит штатно
  - контрактно-чувствительные spec'и `jwt-auth.guard` и OpenAPI schemas синхронизированы с текущей реализацией
  - backend change packages больше не стартуют из состояния с красным test gate
- Exit criteria:
  - `pnpm -C /data/qadam-core test` проходит штатно
  - контрактно-чувствительные spec'и auth/profile/guards синхронизированы с текущей реализацией
  - backend change packages закрываются без ручных оговорок по test gate
- Доказательства:
  - [Roadmap](./roadmap.md)
  - [Change package standard](../governance/change-package-standard.md)

### CP-007 — Repo-side CI gate и PR review discipline

- Статус: `[x] completed`
- Суть: перевести quality gate из только локального процесса в обязательный репозиторный workflow и зафиксировать PR review/security gate как формальный контур
- Факт исполнения:
  - в `qadam-core` добавлен `.gitea/workflows/quality-gate.yml`
  - в `qadam-core` добавлен `.gitea/PULL_REQUEST_TEMPLATE.md`
  - governance и CI rules синхронизированы с обязательным repo-side quality gate и security review checklist
  - на сервере поднят `act_runner` как `systemd`-сервис `qadam-core-runner`
  - repo runner `qadam-core-runner-01` зарегистрирован в Gitea и находится в статусе `online`
  - первый workflow run `Quality Gate` для `qadam-core` завершился в Gitea со статусом `success`
- Exit criteria:
  - существует обязательный workflow, который гоняет `check-types`, `test`, `build`, `export:openapi` и `check:docs`
  - первый успешный run этого workflow подтверждён в Gitea
  - PR review/security checklist используется как обязательный слой для нетривиальных change packages
- Доказательства:
  - [Change package standard](../governance/change-package-standard.md)
  - [Инженерные принципы](../governance/engineering-principles.md)
  - [CI/CD и deployment rules](../Agents/rules/ci-deployment.md)

## 2. Contract и API foundation

### CP-101 — OpenAPI и contract workflow

- Статус: `[x] completed`
- Суть: backend публикует OpenAPI и Swagger, frontend может синхронизировать generated types
- Факт исполнения:
  - живой OpenAPI на `https://qadam.2fab.app/api/openapi.json`
  - versioned artifact в `qadam-core/apps/api/openapi/openapi.json`
  - frontend handoff и frontend changelog работают как обязательный процесс
- Доказательства:
  - [Памятка для frontend-команды](../frontend/frontend-handoff.md)
  - [Change Log для frontend-команды](../frontend/frontend-change-log.md)

### CP-102 — Registration API и профильные контракты buyer/seller

- Статус: `[x] completed`
- Суть: backend закрывает новый registration flow и профили buyer/seller
- Факт исполнения:
  - новые auth routes опубликованы и описаны
  - buyer/seller profile flows переведены на канонические write-contracts
- Доказательства:
  - [Требования к registration API](../product/requirements-api-registration.md)
  - [Карта API-маршрутов](../architecture/api-routes.md)

### CP-103 — Frontend adoption нового registration/profile контракта

- Статус: `[~] in progress`
- Суть: отдельная frontend-команда должна довести `qadam-web` до нового backend-контракта
- Текущее положение:
  - backend и OpenAPI готовы
  - handoff и frontend changelog подготовлены
  - текущие красные зоны `qadam-web` разложены в отдельный remediation backlog для frontend-команды
  - buyer/seller profile response schemas уже закрыты и больше не считаются blocker'ом по OpenAPI coverage
  - `auth auxiliary`, `staff`, `reference`, `seller items` и `admin` response schemas уже закрыты и доступны frontend-команде через OpenAPI artifact
  - активных backend-blocker'ов по OpenAPI response coverage больше нет; оставшаяся работа лежит на внедрении этого контракта во frontend
  - финальное UI-принятие зависит от внешней frontend-команды
- Exit criteria:
  - buyer/seller registration UI не использует legacy `/auth/register`
  - buyer profile flow использует `POST/PATCH /me/profile`
  - seller profile flow использует канонический `PATCH`
  - profile screens не держат ручные transport-типы там, где backend уже публикует response schemas
- Доказательства:
  - [Памятка для frontend-команды](../frontend/frontend-handoff.md)
  - [Change Log для frontend-команды](../frontend/frontend-change-log.md)
  - [Frontend adoption backlog](../frontend/frontend-adoption-backlog.md)

### CP-104 — Закрытие OpenAPI response gaps для remaining core/admin endpoints

- Статус: `[x] completed`
- Суть: довести OpenAPI coverage до уровня, где web перестаёт держать ручные transport types на оставшихся buyer/seller/admin flows
- Факт исполнения:
  - базовый OpenAPI и versioned artifact работают как единый contract layer
  - buyer/seller profile, `Auth auxiliary flows`, `Staff`, `Reference data`, `Seller Items`, `Admin Dashboard` и `Admin Moderation` публикуют response `content` в OpenAPI
  - `pnpm -C /data/qadam-core export:openapi` подтверждает, что активный frontend-значимый response-gap backlog составляет `0`
  - `204 No Content` delete-маршруты остаются техническим хвостом, но не считаются blocker'ом для generated contract layer
- Exit criteria:
  - все endpoint'ы из `openapi-gaps.md` публикуют response `content` в OpenAPI там, где frontend ожидает response body
  - после `pnpm export:openapi` web может перегенерировать типы без ручных response-заглушек для этих flows
  - `Seller Items`, `Admin Dashboard` и `Admin Moderation` больше не считаются manual-contract зоной
- Доказательства:
  - [OpenAPI gaps](../architecture/openapi-gaps.md)
  - [Карта API-маршрутов](../architecture/api-routes.md)
  - [Памятка для frontend-команды](../frontend/frontend-handoff.md)

## 3. MVP hardening

### CP-201 — Buyer profile flow end-to-end

- Статус: `[ ] planned`
- Exit criteria:
  - корректный UI для отсутствующего buyer profile
  - children/interests flow без legacy assumptions
  - regression checks на уровне backend и web

### CP-202 — Seller item и moderation flow

- Статус: `[x] completed`
- Суть: seller item lifecycle и базовый admin moderation flow доведены до предсказуемой status-модели без скрытых переходов
- Факт исполнения:
  - у item status-модели появился явный `DRAFT`, поэтому create/update больше не отправляют айтем на модерацию молча
  - seller теперь явно управляет переходом `DRAFT -> PENDING` через `submit` и может вернуть `PENDING -> DRAFT` через `withdraw`
  - seller не может редактировать айтем в статусе `PENDING`; backend требует сначала отозвать его с модерации
  - seller item responses публикуют `latestModerationRecord`, чтобы UI видел последнюю причину/комментарий модерации
  - admin approve/reject работают только для `PENDING` айтемов и режут повторную модерацию как статусный конфликт, а не как неявное поведение
- Exit criteria:
  - seller item workflow предсказуем от создания до модерации
  - admin moderation не ломает seller UX
  - статусы и причины отклонения доведены до рабочего сценария
- Доказательства:
  - [Карта API-маршрутов](../architecture/api-routes.md)
  - [Change Log для frontend-команды](../frontend/frontend-change-log.md)

### CP-203 — Admin operational surface

- Статус: `[x] completed`
- Суть: admin operational surface доведён до реального рабочего контура вокруг seller status management и moderation
- Факт исполнения:
  - backend публикует `GET /admin/sellers` с фильтрами `search`, `status`, `sellerType`, `page`, `limit`
  - admin seller summary теперь отдаёт `displayName`, contact fields, `telegramConnected` и item counters, поэтому status management больше не держится на ручном знании `sellerId`
  - `PATCH /admin/sellers/:sellerId/status` подтверждён runtime smoke-сценарием `ACTIVE -> UNDER_REVIEW` на временном seller-аккаунте
  - после admin status change seller-facing защищённые маршруты теперь возвращают явные `ACCOUNT_UNDER_REVIEW` / `ACCOUNT_BLOCKED`, а не generic `Invalid or expired token`
  - admin API остаётся полностью покрыт contract-layer, а `GET /admin/sellers` добавлен в OpenAPI и карту API
- Exit criteria:
  - базовые admin сценарии предсказуемы
  - account status management и moderation работают end-to-end
  - admin API покрыт contract-layer и smoke checks
- Доказательства:
  - [Карта API-маршрутов](../architecture/api-routes.md)
  - [Change Log для frontend-команды](../frontend/frontend-change-log.md)

### CP-204 — Web smoke/e2e baseline

- Статус: `[~] in progress`
- Факт исполнения:
  - в `qadam-web` добавлен минимальный stage-oriented smoke baseline `pnpm smoke:web:flows` без отдельного browser runner
  - baseline уже проверяет runtime health, публичные страницы `/`, `/login`, `/register`, `/forgot-password` и role-based web flows buyer/seller/admin через реальный login и cookies
  - public item и public seller profile включены как stage-driven слой через env, без правок кода
  - живой stage-прогон уже подтверждён: `10` из `10` проверок (`runtime health`, public pages, public item, public seller profile, buyer dashboard, seller dashboard, admin dashboard) завершились успешно
- Exit criteria:
  - есть automated smoke/e2e для ключевых buyer/seller/admin flows
  - regressions ловятся до production
- Открытый хвост:
  - baseline ещё нужно встроить во внешний stage release-контур, чтобы он выполнялся автоматически после deploy на проверочный сервер
  - это пока smoke layer, а не полноценный browser-level e2e с пользовательскими интеракциями

### CP-205 — Reviews и status model

- Статус: `[~] in progress`
- Факт исполнения:
  - в `qadam-core` введена backend status-модель `Review`: новые отзывы создаются в `PENDING`, исторические существующие отзывы мигрированы в `PUBLISHED`
  - публичный `GET /catalog/items/:slug/reviews` и rating/reviews aggregates в catalog/public seller profile теперь учитывают только `PUBLISHED`
  - buyer cabinet `GET /me/reviews` теперь публикует `status` и `moderationNote`, чтобы frontend не держал скрытое допущение "любой отзыв сразу опубликован"
  - backend публикует admin review moderation endpoints `GET /admin/moderation/reviews`, `GET /admin/moderation/reviews/:id`, `PATCH /admin/moderation/reviews/:id`
  - seller review surface доведён на backend: `GET /seller/reviews`, `PATCH /seller/reviews/:id/reply`, `POST /seller/reviews/:id/complaint`
  - complaint-driven moderation уже замкнута end-to-end: seller complaint переводит отзыв в `PENDING_MODERATION`, скрывает его из public aggregates, а admin moderation принимает финальное решение с фиксацией complaint resolution
- Exit criteria:
  - reviews submission, moderation и public display следуют явной status-модели
  - buyer cabinet и публичные страницы показывают согласованное review-состояние без скрытых допущений
  - review API и UI покрыты contract-layer и smoke/e2e по уровню риска
- Открытый хвост:
  - frontend-команда должна внедрить buyer/public/admin/seller review UI под новый контракт
  - review-сценарии пока не включены в автоматизированный web smoke/e2e baseline

### CP-206 — Public seller profile и SEO baseline

- Статус: `[~] in progress`
- Факт исполнения:
  - public seller profile backend очищен от внутренних seller-only полей и теперь публикует отдельный public contract вместо прямого reuse приватного seller profile shape
  - `GET /sellers/:id` теперь отдаёт explicit `seo` block: `title`, `description`, `canonicalUrl`, `openGraph`, `jsonLd`
  - canonical/SEO URLs могут браться из `PUBLIC_WEB_BASE_URL`, а при его отсутствии backend выводит базовый public URL из `NEXT_PUBLIC_API_URL`
  - non-active seller profile режется как `404`, а hidden seller addresses не включаются в публичный payload
- Exit criteria:
  - public seller profile работает как стабильная SSR entry point
  - meta/SEO baseline описан явно и не держится на скрытых предположениях
  - public seller profile включён в smoke/e2e baseline там, где это оправдано риском
- Открытый хвост:
  - frontend-команда должна внедрить backend-generated `seo` block в SSR metadata/public seller page
  - browser-level e2e для public seller profile и metadata всё ещё не закрыт, даже несмотря на успешный stage smoke baseline

## 4. Delivery и operations maturity

### CP-301 — Image-based delivery и docker contour

- Статус: `[~] in progress`
- Факт исполнения:
  - на production-сервер установлен Docker Engine с compose plugin, но container runtime ещё не заменил канонический `systemd`-контур приложений
  - Docker `data-root` вынесен на отдельный диск `/mnt/qadam100gb/docker`, поэтому image build больше не конкурирует с production runtime за корневой раздел
  - в `qadam-core` канонизирован `apps/api/Dockerfile`: runtime user, `dumb-init`, healthcheck, отдельные helper scripts для migrations/seed и отказ от legacy entrypoint
  - в `qadam-core` добавлен первый канонический deployment manifest `deploy/compose/docker-compose.api-runtime.yml` для `api`, `migrate` и `seed`
  - для `api` добавлен канонический image build script `deploy/scripts/build-api-image.sh`, который собирает образ с OCI labels и результативным тегом `qadam-core-api:git-<commit>`
  - для `api` добавлен publish script `deploy/scripts/publish-api-image.sh`, который публикует versioned image tag в Gitea Container Registry `git.2fab.app/eldar/qadam-core-api`
  - для `api` добавлен compose-based shadow smoke `deploy/scripts/smoke-api-runtime-compose.sh`, который поднимает runtime-manifest на `127.0.0.1:5002` с production env и подтверждает `health/ready` + `metrics` без cutover host-level `qadam-api`
  - API image `qadam-core-api:cp301-smoke` собран на сервере, контейнерный smoke подтверждён: `/app/bin/run-migrations.sh` проходит, а `GET http://127.0.0.1:5002/api/v1/health` отвечает `200`
  - registry path подтверждён по факту: `git.2fab.app/eldar/qadam-core-api:registry-smoke-4805b86` успешно опубликован и может использоваться как source image для shadow smoke
  - для `api` добавлены source-controlled cutover/rollback-скрипты `deploy/scripts/cutover-api-to-container.sh`, `deploy/scripts/rollback-api-to-systemd.sh` и nginx upstream switcher `deploy/scripts/set-api-upstream.sh`
  - на сервере поднят `qadam-api-container.service`, а `/etc/qadam/qadam-api-runtime.env` зафиксировал канонический registry image tag для container runtime
  - public `/api/*` трафик реально переведён на `qadam-api-container.service`: host nginx использует `qadam_api_backend -> 127.0.0.1:5002`, legacy `qadam-api.service` остановлен и оставлен как rollback path
  - для `qadam-web` добавлены `Dockerfile`, build/publish/smoke scripts и runtime manifest `deploy/compose/docker-compose.web-runtime.yml`
  - registry namespace `git.2fab.app/eldar/qadam-web-app` подтверждён по факту: product web image опубликован, затем независимо вытянут через `docker pull` и поднят как shadow runtime
  - `qadam-web` shadow smoke уже проходит от registry image на `127.0.0.1:3002` без локального `next build` на production-сервере
  - backend bootstrap больше не требует writable локальный uploads path при `OBJECT_STORAGE_DRIVER=s3`: static `/uploads` и создание каталогов включаются только для local driver
  - self-hosted runner `qadam-core-runner` теперь имеет доступ к Docker daemon, а repo-side workflow дополнен отдельным job `API Container Smoke`
  - repo-side workflow дополнен job `Publish API Image`, который после зелёного smoke публикует versioned image tag в Gitea Container Registry на `push` в `main`
  - container smoke стал self-contained: workflow поднимает временные PostgreSQL и Redis контейнеры, прогоняет migrations внутри image и проверяет `health/ready` и `metrics` без зависимости от production БД
- Exit criteria:
  - есть управляемый app-level image-based delivery
  - host-based deploy перестаёт быть единственным рабочим контуром
  - `qadam-web` и `qadam-roadmap` получают такой же канонический image-based runtime или documented mixed-runtime status перестаёт быть временной промежуточной фазой

### CP-302 — Backup automation и off-host retention

- Статус: `[x] completed`
- Факт исполнения:
  - в `qadam-core` добавлен исполняемый backup runtime `scripts/backup-runtime.mjs` и root-команда `pnpm backup:run`
  - на сервере подняты `qadam-backup.service` и `qadam-backup.timer`; timer ежедневно запускает backup baseline без ручного вмешательства
  - backup-сценарий снимает `pg_dump`, архивы API/roadmap storage, roadmap feedback overlay и runtime config (`env`, `systemd`, `nginx`)
  - backup локально верифицируется через `pg_restore --list` и `SHA256SUMS`, а результат последнего прогона фиксируется в `/var/lib/qadam-backup/state.json`
  - off-host archive реально выгружается в S3-compatible storage по префиксу `backups/runtime/<STAMP>.tar.gz`
  - retention policy уже применяется автоматически: `7` дней локально и `30` дней off-host
- Exit criteria:
  - backup перестаёт быть только ручной процедурой
  - есть off-host copy и retention policy
- Доказательства:
  - [Runbook резервного копирования и восстановления](../operations/backup-restore-runbook.md)
  - [Environment matrix](../operations/environment-matrix.md)
  - [Текущее состояние](./current-state.md)

### CP-303 — Monitoring и alerting baseline

- Статус: `[x] completed`
- Факт исполнения:
  - backend уже публикует отдельный monitoring baseline: `GET /health`, `GET /health/live`, `GET /health/ready`
  - readiness probe на production реально проверяет primary PostgreSQL и Redis и отдаёт machine-readable component status с latency
  - backend публикует Prometheus-compatible `GET /metrics`, но сознательно режет его на `403` для не-loopback запросов и оставляет доступным только с сервера
  - в API появился базовый HTTP/runtime metrics contour на `prom-client`: default process metrics, `http_requests_total` и `http_request_duration_seconds`
  - для operational alerts подготовлен delivery channel: в production зафиксирован `TELEGRAM_ALERT_CHAT_ID`, а в `qadam-core` появился reusable sender `pnpm telegram:alert`
  - на сервере подняты `qadam-monitor.service` и `qadam-monitor.timer`: они внешне проверяют `qadam.2fab.app`, `api/v1/health/ready`, `qadam-roadmap.2fab.app` и TLS обоих доменов
  - runtime monitor сохраняет state в `/var/lib/qadam-monitor/state.json` и шлёт алерты в Telegram только на изменение статуса или recovery, без постоянного спама
- Exit criteria:
  - есть uptime/TLS monitoring
  - есть operational visibility по API, web и roadmap runtime

### CP-304 — Product media object storage

- Статус: `[x] completed`
- Факт исполнения:
  - в `qadam-core` реализован storage adapter `local | s3` для `POST /api/v1/upload/image`
  - production runtime `qadam-api` переведён на `OBJECT_STORAGE_DRIVER=s3` с S3-compatible backend `DigitalOcean Spaces`
  - upload contract сохранён: backend по-прежнему возвращает `{ url }`, а frontend должен считать `url` opaque public URL
  - живой smoke upload подтвердил публично читаемый объект по absolute Spaces URL и успешное удаление тестового объекта после проверки
- Exit criteria:
  - `POST /api/v1/upload/image` работает через storage adapter и S3-совместимый backend
  - seller logo/photo и item image больше не зависят от локального `/var/lib/qadam-core/uploads`
  - внешний API-контракт upload endpoint остаётся стабильным: backend по-прежнему возвращает `{ url }`
  - S3 credentials не зашиваются в Dockerfile или образ, а поставляются только через runtime env/secrets
  - storage `qadam-roadmap` осознанно остаётся отдельным контуром и не мигрируется в этот пакет по умолчанию

## 5. Правило обновления этого документа

Этот документ обновляется:

- после завершения change package, который реально закрыл этап;
- после появления блокера, который переводит checkpoint в `[!] blocked`;
- после rollback, если фактический статус этапа ухудшился;
- одновременно с обновлением [project-change-log.md](./project-change-log.md).
