spec: define versioned deployment bundle
Conventional commits / commits (pull_request) Successful in 12s
Specs security / security (pull_request) Successful in 8s
Specs verify / verify (pull_request) Successful in 11s
Specs security / security (push) Successful in 7s
Specs verify / verify (push) Successful in 10s

This commit was merged in pull request #14.
This commit is contained in:
2026-09-20 18:49:25 +03:00
parent e7a3c30b5d
commit 7f40372c3f
5 changed files with 458 additions and 0 deletions
@@ -0,0 +1,2 @@
schema: wealth-ledger-sdd
created: 2026-09-20
@@ -0,0 +1,179 @@
## Context
Согласован отдельный репозиторий `deploy` и команда `./deploy.sh <tag>` по SSH.
Тег принадлежит комплекту, а не компоненту; соответствие пары хранится в
`release.env`. Решения приняты в обсуждении с владельцем и записаны в ADR 016
репозитория docs; эти артефакты готовят реализацию, не подтверждают её выполнение.
На сервере уже есть Compose project `wealthledger`, SQLite, постоянные ключи
и read-only mount `certs/tbank-ca.pem` через `TBANK_CA_CERT_PATH`. Внешний HTTPS
и Docker подготовлены. Образы публикуются в Nexus независимыми релизами.
`containerized-application-delivery` остаётся отдельным active change;
его runtime-контракт используется без редактирования или преждевременного archive.
## Goals / Non-Goals
**Goals:** небольшой комплект с воспроизводимой парой образов, одним entrypoint,
безопасным обновлением SQLite и понятным восстановлением существующего сервера.
**Non-Goals:** provisioning, CI/CD автодеплой, новый registry, Kubernetes,
Ansible-инфраструктура, blue-green, замена БД, backup daemon, автоматическая
ротация копий, собственная система миграций или управление HTTPS.
## Decisions
### Комплект и постоянные ресурсы
Минимальное содержимое `deploy`: `compose.yaml`, `release.env`, `.env.example`,
`deploy.sh`, README и одна исполняемая проверка сценариев. Compose использует
готовые frontend/backend images, migration запускается из backend image.
Production-комплект самодостаточен и не требует соседних checkout.
Существующие root Compose-файлы для development не удаляются этим change.
На сервере checkout располагается отдельно от `data/`, `certs/`, `backups/`
и защищённого server env. Например, checkout — `/opt/wealthledger/deploy`,
остальное — соседние пути в `/opt/wealthledger`. Существующий data path
не переименовывается; пути bind mounts абсолютные. Ключи не генерируются
повторно. Root filesystem остаётся read-only, процесс non-root, backend
не получает host port; сертификат доверия монтируется read-only без отключения TLS.
`release.env` содержит только полные versioned image references из Nexus;
`latest` исключён. Server env хранит настройки и секреты, но не выбирает пару.
Переменные окружения вызывающего shell не должны переопределять выбранные
images или Compose project. Env-файлы обрабатываются Compose как данные,
не выполняются shell через `source`. Полный config с секретами не печатается.
### Исполнение выбранной версии
Entrypoint получает тег только из настроенного доверенного `origin`, разрешает
его в commit и использует содержимое этого commit для всей операции, включая
версию deploy-логики. Полученный ранее тег не перезаписывается при расхождении.
Работа идёт из временного snapshot выбранного commit; запущенный скрипт и
операторские изменения checkout не перезаписываются переключением ветки.
Compose file, env files и project `wealthledger` задаются явно: временная
директория не должна создать второй project или изменить пути данных.
До остановки выполняются проверки входов, ресурсов, конфигурации и загрузка
образов. Поддерживаемый target — один Linux Docker host; Git, Bash и `flock`
входят в перечисленные README предпосылки. Архитектура опубликованных образов
должна соответствовать серверу; multi-platform поддержку этот change не добавляет.
### Обновление, повтор и состояние
Один `flock` на установку исключает конкурирующие запуски. Не добавляется
сервис блокировок. Файл блокировки и минимальные служебные записи находятся
вне checkout, с ограниченными правами, без секретов.
Нужны только запись последнего успешного релиза и маркер незавершённого
обновления: выбранный commit, фактические images, этап и расположение backup.
Маркер сохраняется до первого изменения runtime; успешная запись атомарно
обновляется только после готовности затронутых сервисов. При прерывании
маркер остаётся для диагностики; новый запуск требует ручного разбора по README.
Это не журнал всех deployments и не отдельная база состояний.
При первом переходе предыдущая версия описывается фактической парой images:
несуществующий тег прежнего комплекта не придумывается.
План изменений сверяет желаемые images и Compose service configuration
с фактическими контейнерами. Для сравнения использовать native Compose
service hash и Docker labels, если их соответствие подтверждено проверкой.
Секреты не записываются в служебные файлы. Отсутствие достоверного baseline
не разрешает frontend-only shortcut: первый переход с серверного Compose
выполняется как полное обновление с backup. Успешная запись сама по себе
не заменяет проверку фактически работающих контейнеров.
При обновлении API: stop backend → проверка отсутствия migration writer →
новый уникальный каталог backup → копия всего data directory, включая WAL/SHM →
одноразовая migration → backend readiness. Для существующей установки отсутствие
`wealthledger.db` — ошибка. Только явный первый запуск `--init` на пустом
каталоге без прежнего runtime допускает создание новой базы без backup.
Копия отмечается пригодной для восстановления только после успешного окончания
копирования; незавершённый каталог не выдаётся за готовый backup.
Неизменный frontend не пересоздаётся; обновление только frontend не запускает
зависимости. При обновлении обоих компонентов новый frontend запускается после
готовности backend. Healthchecks используются из готовых images через Compose;
новый HTTP endpoint не нужен. Browser smoke через HTTPS остаётся отдельной
проверкой выпуска: container health не доказывает login/refresh.
Backup failure и migration failure прекращают последовательность. При провале
readiness backend останавливается. Провал frontend не останавливает исправный
backend. Любая ошибка оставляет релиз неподтверждённым, текущую БД и backup
неизменёнными процедурой восстановления; диагностика не выводит secrets.
### Выпуск и инструкция
После публикации компонентов оператор меняет пару в `release.env`, проверяет
её на disposable установке и выпускает отдельный неизменяемый `vX.Y.Z` тег
`deploy`. Компонентные pipeline не связываются автоматическим publisher.
Первый комплект закрепляет реально доступные образы, не демонстрационные версии.
README описывает подготовку Docker/Git/registry/HTTPS, разрешения каталогов,
сохранение ключей, подключение действующего Compose project, install/update,
диагностику незавершённой операции, ручную очистку backups и recovery.
## UI/UX Applicability and Package
**Applicability and scope:** `N/A` — браузерный интерфейс и его состояния
не меняются; оператор использует терминал и README. Макеты не нужны.
## Delivery Map
| Area | Status | Architectural outcome or N/A reason |
| --- | --- | --- |
| frontend | `N/A` | Готовый image переиспользуется, UI и pipeline не меняются |
| backend | `N/A` | Готовые runtime, healthcheck и migrations переиспользуются |
| specs | `affected` | Контракт deployment-bundle и план его проверки |
| docs | `affected` | ADR 016 и обновлённый эксплуатационный маршрут после реализации |
| deploy | `affected` | Самодостаточный комплект, сценарии проверки и README |
| root | `affected` | Только навигация README к production-комплекту; development Compose не меняется |
Уточнение старого шаблона: обязательные четыре области сохранены; добавлен
реальный repository owner `deploy`, явно выбранный пользователем, и `root`
для необходимой поправки workspace-навигации. В Tasks используются `[deploy]`
и `[root]`, а не фиктивный `[backend]` или `[workspace]`.
Это локальное расширение перечня owners для данного change; глобальная
переработка SDD schema и process specs в scope не входит. Native structural
validation не доказывает соответствие этому уточнению или semantic approval.
## Solution Assessment
| Вариант | Fit, зрелость и безопасность | Стоимость и ограничения |
| --- | --- | --- |
| Только Docker Compose | Зрелый runtime, зависимости, healthchecks и выбор images уже покрыты | Не выполняет stop/copy/migrate и не исключает две операторские операции |
| Bash + Git + Compose + flock | Native инструменты владеют загрузкой, запуском и блокировкой; shell связывает существующий безопасный порядок | Небольшой branching требует проверок ошибок и прерываний; подходит одному Linux host |
| Ansible | Зрелый механизм управления серверами, но backup/migration порядок всё равно надо определить | Добавляет control environment, inventory и роль при уже подготовленном одном сервере |
Выбран второй вариант. Чистого Compose недостаточно для согласованной копии
при остановленной записи; отдельный provisioning stack не соответствует
согласованному отсутствию управления серверной инфраструктурой. Новый package
manager, framework или сторонняя runtime-зависимость не вводятся.
## Risks / Trade-offs
- Краткий простой и единственный writer сохраняются; внешние процессы записи
должны быть исключены оператором при подключении существующей установки.
- Локальный backup не спасает от потери диска; свободное место и ручная очистка
остаются эксплуатационной обязанностью. Ошибка копирования блокирует миграции.
- Native hash может измениться между версиями Compose: сомнение приводит
к полному безопасному обновлению, а не к пропуску backup.
- Прерванная migration требует разбора, а не слепого повторения команды.
Ни служебный маркер, ни последний успешный тег не являются копией базы.
- Доверие к Git origin и неизменяемым Nexus release tags остаётся существующей
границей поставки; собственная система подписей здесь не добавляется.
## Migration Plan
Подготовить самодостаточный комплект и проверить безопасный lifecycle на
disposable данных. Затем обновить docs, сохранив development-инструкцию.
Для действующего сервера сохранить старые настройки и соответствие images,
подключить существующие абсолютные пути и project, выполнить первый полный
переход с backup. Старый способ запуска не выполнять параллельно.
### Rollback Contract
| Field | Decision |
| --- | --- |
| Status | `required` |
| Trigger | Ошибка migration/readiness, прерывание либо выявленная регрессия |
| Actions | Остановить API при риске для данных; отдельно выбрать совместимый image либо explicit restore; незавершённый маркер снять только после проверки состояния |
| Data safety | Не восстанавливать БД автоматически; перед explicit restore сохранить текущий каталог, выбрать копию, подтвердить потерю новых записей и наличие прежнего ключа |
| Verification | На отдельной копии проверить целостность БД, совместимость схемы, auth/cards и расшифровку подключения; после восстановления повторить smoke через HTTPS |
@@ -0,0 +1,40 @@
## Why
После выпуска компонентов оператор вручную выбирает совместимые версии
и повторяет порядок обновления. Нужен один выбранный релиз всей установки
и одна команда, сохраняющая данные и настройки существующего сервера.
## What Changes
- Версионируемый комплект закрепляет проверенную пару версий интерфейса и API.
- Оператор явно выбирает релиз; публикация компонента не обновляет сервер.
- Обновление выполняется одной командой с предварительными проверками,
сохранением данных и диагностируемым результатом.
- Обновление только интерфейса не прерывает работу API и не затрагивает базу.
- Перед обновлением API создаётся согласованная резервная копия. Неудачное
обновление не запускает автоматическое восстановление или откат.
- Инструкция охватывает подготовку сервера, первую установку, подключение
существующей установки, выпуск комплекта, обновление и ручное восстановление.
Non-goals: настройка серверной инфраструктуры командой развёртывания,
автоматическое обновление по выходу компонента, непрерывная доступность,
несколько экземпляров API, автоматическая очистка копий и внешнее backup-хранилище.
## Capabilities
### New Capabilities
- `specs/deployment-bundle/spec.md`: выбор полного релиза, безопасная автоматизация
обновления и сохранение существующей установки.
### Modified Capabilities
Нет. Новый контракт дополняет контейнерную поставку; существующий active change
`containerized-application-delivery` и его capability `application-delivery`
не изменяются и не архивируются в рамках этой работы.
## Impact
Основной пользователь — оператор одной установки WealthLedger. Изменяются
комплект развёртывания, эксплуатационная документация и требования к обновлению.
Прикладные функции интерфейса и API остаются прежними.
@@ -0,0 +1,169 @@
## Purpose
Определяет установку выбранного релиза WealthLedger одной командой,
сохранение состояния сервера и безопасный результат успешного или неудачного обновления.
## ADDED Requirements
### Requirement: Релиз определяет полную пару компонентов
Комплект SHALL иметь отдельную неизменяемую версию, однозначно определяющую
проверенную пару версий интерфейса и API. Оператор MUST явно выбирать релиз
комплекта. Публикация компонента MUST NOT автоматически выпускать комплект
или обновлять сервер; локальные настройки MUST NOT незаметно подменять пару.
#### Scenario: Установка выбранного релиза
- **WHEN** оператор выбирает опубликованный релиз комплекта
- **THEN** устанавливается ровно закреплённая в нём пара, даже если опубликованы
более новые версии компонентов
#### Scenario: Отсутствующий или изменённый релиз
- **WHEN** выбранный релиз отсутствует либо ранее полученная версия теперь
указывает на другое содержимое
- **THEN** операция завершается ошибкой до изменения работающей установки
### Requirement: Проверки предшествуют изменению установки
Одна команда SHALL выполнять установку выбранного релиза на подготовленном
сервере. До остановки приложения она MUST проверить доступность комплекта,
его компонентов, обязательных настроек и постоянных ресурсов.
Отсутствующая база существующей установки MUST NOT трактоваться как новая
пустая установка. Секреты MUST NOT попадать в диагностический вывод.
#### Scenario: Компонент недоступен
- **WHEN** выбранный компонент нельзя получить или обязательный сертификат
либо каталог данных недоступен
- **THEN** обновление завершается с понятной ошибкой, не останавливая
работающие компоненты и не создавая замену отсутствующим данным
#### Scenario: Новая установка
- **WHEN** оператор явно подготовил новую установку с отдельным пустым
хранилищем и необходимыми настройками
- **THEN** команда подготавливает данные и запускает выбранный релиз,
не требуя предыдущей установленной версии
### Requirement: Существующая установка сохраняет состояние
Подключение существующей установки и последующие обновления MUST сохранять
её пользовательские данные, сессии, ключи шифрования и настройки доверия
внешнему провайдеру. Эти ресурсы MUST храниться независимо от обновляемого
комплекта. Команда MUST NOT запускать второй API на той же базе или генерировать
новые ключи вместо действующих.
#### Scenario: Переход с прежнего способа запуска
- **WHEN** оператор подключает существующую установку к комплекту
- **THEN** используются её прежние данные и ключи; после запуска доступны
существующие записи и расшифровка сохранённого подключения
### Requirement: Обновление затрагивает только изменённые компоненты
Если меняется только интерфейс, команда MUST сохранять работающий API,
не изменять данные и не запускать миграции или резервное копирование.
Изменение настроек API SHALL считаться изменением API, даже при той же версии.
Обновление только API MUST NOT пересоздавать неизменный интерфейс.
Повторное применение уже установленного исправного релиза с теми же настройками
MUST NOT выполнять ненужное обновление или резервное копирование.
#### Scenario: Новый интерфейс при прежнем API
- **WHEN** выбранный релиз меняет только интерфейс, а API и его настройки
подтверждённо совпадают с установленными
- **THEN** обновляется только интерфейс, API продолжает работу без миграций
и резервного копирования для этой операции
#### Scenario: Та же версия API с другими настройками
- **WHEN** версия API прежняя, но его настройки изменились
- **THEN** применяется безопасная процедура обновления API
#### Scenario: Повтор успешно установленного релиза
- **WHEN** выбран уже установленный исправный релиз без изменений настроек
- **THEN** команда сообщает, что обновление не требуется, без остановки
компонентов и без новой резервной копии
### Requirement: Обновление API сохраняет согласованную копию
Перед обновлением API существующей установки команда MUST остановить запись
и успешно сохранить согласованную копию всех постоянных данных до миграций.
Копии MUST находиться вне обновляемого комплекта; команда MUST NOT удалять
или перезаписывать прежние копии автоматически. Неуспешная копия MUST
блокировать миграции. Новая пустая установка не требует фиктивного backup.
#### Scenario: Подготовка обновления API
- **WHEN** выбран релиз с изменением API существующей установки
- **THEN** до миграций сохраняется отдельная согласованная копия при отсутствии
пишущих процессов, и оператор получает её расположение
#### Scenario: Копию сохранить не удалось
- **WHEN** резервное копирование завершилось ошибкой
- **THEN** миграции и запуск новой версии не выполняются, существующие данные
и прежние копии сохраняются, операция сообщает ошибку
### Requirement: Неудачное обновление не скрывает риск для данных
Ошибка миграции MUST блокировать запуск новой версии API. Если после миграции
API не готов, команда MUST остановить неисправный API и завершиться с ошибкой.
Любая неуспешная проверка обновляемого компонента MUST блокировать отметку
релиза как успешно установленного. Диагностика SHALL указывать этап сбоя,
предыдущую версию и доступный backup без раскрытия секретов.
Автоматический откат или восстановление данных MUST NOT выполняться.
#### Scenario: Миграция не прошла
- **WHEN** подготовка схемы завершается ошибкой
- **THEN** API остаётся остановленным, а результат содержит сведения для
отдельного решения о восстановлении
#### Scenario: Новый API не готов
- **WHEN** миграция завершилась успешно, но проверка готовности API не прошла
- **THEN** неисправный API останавливается без автоматического запуска старой
версии или восстановления базы
#### Scenario: Неудачное обновление интерфейса
- **WHEN** при обновлении только интерфейса его проверка запуска не прошла
- **THEN** операция завершается ошибкой, API продолжает работу, база не
восстанавливается, релиз не отмечается успешно установленным
### Requirement: Повтор и конкуренция не нарушают порядок обновления
Команда MUST исключать одновременные изменения одной установки несколькими
запусками. Прерванное или неуспешное обновление MUST отличаться от успешно
установленного релиза. Повтор команды после потенциального изменения данных
MUST NOT скрывать незавершённую операцию или автоматически восстанавливать данные.
#### Scenario: Второе обновление
- **WHEN** для установки уже выполняется обновление
- **THEN** второй запуск завершается без изменения её состояния
#### Scenario: Процесс прерван после начала обновления
- **WHEN** предыдущая операция прервана после остановки API или начала миграции
- **THEN** следующий запуск сообщает о незавершённой операции и необходимости
проверки состояния до нового обновления
### Requirement: Оператор располагает полным порядком эксплуатации
Инструкция комплекта SHALL описывать последовательность подготовки сервера,
первой установки и подключения существующей установки, подготовку проверенной
пары и выпуск релиза, обновление, диагностику, откат версии и отдельное
восстановление базы. Она MUST указывать сохранение ключей, ручную очистку
локальных копий и отсутствие защиты от потери диска только локальным backup.
Восстановление MUST требовать явного выбора копии и принятия потери более
новых данных; откат API — проверки совместимости с текущей схемой.
#### Scenario: Оператор восстанавливает установку
- **WHEN** оператор использует инструкцию после неудачного обновления
- **THEN** он может отдельно выбрать совместимую версию приложения или
восстановление конкретной копии с явным учётом потери новых данных
@@ -0,0 +1,68 @@
## 1. Самодостаточный комплект
- [x] 1.1 [deploy] Создать production Compose, release.env и пример server env, переиспользовав существующие images/migration/healthchecks и сохранив read-only CA mount и постоянные абсолютные пути.
- **Covers:** `deployment-bundle` — `Релиз определяет полную пару компонентов`; `deployment-bundle` — `Существующая установка сохраняет состояние`
- **Verification:** `docker compose --env-file <disposable-server.env> --env-file release.env -f compose.yaml config --quiet`; inspect подтверждает выбранную пару, один backend, общий data mount migration/backend, отсутствие host port API и read-only сертификат; exported image variables не подменяют релиз через entrypoint.
- [x] 1.2 [deploy] Реализовать получение выбранного тега, выполнение snapshot его commit, preflight, flock и минимальные записи состояния в одном deploy.sh согласно Solution Assessment.
- **Covers:** `deployment-bundle` — `Релиз определяет полную пару компонентов`; `deployment-bundle` — `Проверки предшествуют изменению установки`; `deployment-bundle` — `Повтор и конкуренция не нарушают порядок обновления`
- **Verification:** `bash -n deploy.sh`; сценарная проверка отсутствующего/переназначенного тега, недоступного image/CA/data, отказа второго запуска и отсутствия изменений runtime при preflight failure; snapshot использует deploy-логику выбранного commit.
## 2. Безопасный lifecycle и его доказательство
- [x] 2.1 [deploy] Реализовать first install/adoption, выбор изменённых сервисов, stop/backup/migration/readiness и остановку последовательности при ошибке без автоматического rollback.
- **Covers:** `deployment-bundle` — `Проверки предшествуют изменению установки`; `deployment-bundle` — `Существующая установка сохраняет состояние`; `deployment-bundle` — `Обновление затрагивает только изменённые компоненты`; `deployment-bundle` — `Обновление API сохраняет согласованную копию`; `deployment-bundle` — `Неудачное обновление не скрывает риск для данных`; `deployment-bundle` — `Повтор и конкуренция не нарушают порядок обновления`
- **Verification:** На disposable Compose project проверить явный `--init`, adoption существующей тестовой БД, обновление обоих компонентов и API отдельно, неизменность container ID API при frontend-only update, отсутствие повторного backup при healthy no-op; изменение backend config включает backup даже с прежним image.
- [x] 2.2 [deploy] Добавить одну исполняемую проверку tests/deploy.sh без нового test framework и закрепить локальный gate для shell и сценариев обновления.
- **Covers:** `deployment-bundle` — `Релиз определяет полную пару компонентов`; `deployment-bundle` — `Проверки предшествуют изменению установки`; `deployment-bundle` — `Существующая установка сохраняет состояние`; `deployment-bundle` — `Обновление затрагивает только изменённые компоненты`; `deployment-bundle` — `Обновление API сохраняет согласованную копию`; `deployment-bundle` — `Неудачное обновление не скрывает риск для данных`; `deployment-bundle` — `Повтор и конкуренция не нарушают порядок обновления`
- **Verification:** `bash -n deploy.sh tests/deploy.sh`, `bash tests/deploy.sh`, `git diff --check`; fault injection доказывает отсутствие migration после failed copy, отсутствие backend start после failed migration, stop после backend readiness failure, сохранение backend после frontend failure, неизменность данных/старых backups, блокировку конкуренции и незавершённой операции. Проверка использует временные данные; production paths недопустимы.
## 3. Эксплуатация и документация
- [x] 3.1 [deploy] Описать в README подготовку сервера, сохранение старых ключей и Compose project, install/update, выпуск тега с проверенной парой, диагностику, ручной rollback/restore и очистку backups; закрепить реально опубликованную первую пару перед выпуском.
- **Covers:** `deployment-bundle` — `Оператор располагает полным порядком эксплуатации`; `deployment-bundle` — `Релиз определяет полную пару компонентов`
- **Verification:** Пройти инструкцию на изолированной установке с тестовыми данными; проверить login/refresh через HTTPS, сохранение записей и расшифровку тестового подключения прежним ключом, `PRAGMA integrity_check` на копии и explicit restore; сравнить actual images с release.env. Первый тег не публиковать до проверки пары.
- [x] 3.2 [docs] Завершить ADR 016 и glossary, обновить production-раздел контейнерной поставки под deploy с сохранением development-инструкции и границ ручного recovery.
- **Covers:** `N/A` — docs объясняет поставленный механизм из Design и направляет оператора к единственному README комплекта; самостоятельного runtime behavior не добавляет.
- **Verification:** После `nvm use --silent` выполнить `npm run verify` в docs; проверить маршрут README → deployment → ADR и отсутствие обещаний ещё не реализованных операций или дублирующего production runbook.
- [x] 3.3 [root] Направить production-раздел root README к комплекту deploy, сохранив существующие development-команды.
- **Covers:** `N/A` — workspace-навигация должна вести к владельцу production lifecycle из Design; поведение приложения и root tooling не меняются.
- **Verification:** `git diff --check` в root; просмотр маршрута root README → deploy README и docs/architecture/deployment.md, проверка неизменности development-команд.
## 4. Сверка поставки
- [x] 4.1 [specs] Сверить артефакты с фактической реализацией, сохранить coverage всех требований и проверить canonical change без изменения чужого containerized-application-delivery.
- **Covers:** `N/A` — requirements и checklist должны оставаться контрактом реализованного комплекта; задача поддерживает точность SDD, а не вводит новое поведение приложения.
- **Verification:** После `nvm use --silent`: `openspec schema validate wealth-ledger-sdd --verbose`; `openspec validate versioned-deployment-bundle --type change --strict --store wealth-ledger-specs`; `openspec validate --specs --strict --store wealth-ledger-specs`; `npm run verify`; вручную проверить task coverage и соответствие дополнительному owner deploy из Delivery Map.
- [x] 4.2 [workspace] Выполнить итоговую сверку сценариев установки, ошибок и восстановления с готовыми образами и результатами локальных gates затронутых репозиториев.
- **Covers:** `N/A` — объединяет доказательства deploy/specs/docs для согласованной пары, не владеет реализацией какого-либо репозитория и не заменяет independent review.
- **Verification:** На disposable данных сверить успешный update, frontend-only, interrupted update и recovery с Delta Specs; зафиксировать фактические версии и результаты gates. Проверки реального сервера не подменять локальным smoke; production rollout не входит в эту задачу.
## 5. Implementation checkpoint
2026-09-20: implementation-owned задачи выполнены; готово к независимой Validation.
Change остаётся active. Первый тег deploy не опубликован, рабочий сервер не изменялся.
- Deploy: `bash -n deploy.sh tests/deploy.sh`, `bash tests/deploy.sh` — PASS.
Проверены selective update/no-op, config drift, сохранение ключей, symlink guard,
ошибки pull/preflight/copy/migration/backend/frontend, concurrent lock и pending.
- `DOCKER_DEFAULT_PLATFORM=linux/amd64 bash tests/deploy.sh --docker-restore` — PASS:
заменённый bind-каталог читается после force-recreate. Без пересоздания исходный
live repro читал старые данные; README recovery исправлен по этому результату.
- Disposable smoke с реальными Nexus images `0.2.3` — PASS: init, no-op по native
Compose hash, frontend-only/backend-only config update, stopped backup,
`PRAGMA integrity_check`, HTTPS deep link, registration, Secure-cookie и refresh.
- Explicit restore на синтетических данных — PASS: login, карта и её баланс,
отсутствие более новой карты и расшифровка тестового токена штатным TbankCrypto.
Оба тестовых Compose projects удалены; настоящие пользовательские данные
и реальное подключение к T-Bank не использовались.
- OCI revisions совпали с Git tags компонентов: frontend
`f486ae4fb2d06e71779901bde283950542f27ed2`, backend
`0862d56c4e4d9dd554556c3992cc073193020ff1`.
- Среда интеграционной проверки: Docker Desktop, Linux amd64 images через
эмуляцию на arm64 Mac, Compose 5.5.1; host flock в smoke заменён stdlib fcntl.
Это не проверка конфигурации целевого Linux-сервера или его registry login.