WEB board

всеканоны и докиворкеры↗ iOS↗ Легаси
WEB-653 · Дефект · Инфраструктура · web

P1 [bus factor]: перед откатом нужно доказательство, что старое приложение читает новую схему БД (Q28)

Закрыт P1 · важно ведёт: — эпик: WEB-641
Суть
Правила обогащения: WEB-449. Parent WEB-641. Заведено 13.09 по внешнему разбору (подзадача BF-FIX-4).

## Суть
Книга разрешает откат на предыдущий релиз, но нигде не требует доказательства, что ТОТ релиз умеет читать схему БД, которая уже мигрировала вперёд. Если не умеет — откат превращается в аварию вместо спасения.

## Что сделать
В preflight отката: требовать квитанцию «старое приложение прочитало новую схему» для конкретного заявленного пути отката. Нет квитанции — этот откат не разрешается (разрешается только тот, для которого доказательство есть). Квитанция должна получаться воспроизводимой командой, а не быть утверждением в тексте.

## Приёмка
Тест: путь отката без квитанции → отказ с понятной строкой; с квитанцией → разрешено; квитанция от другой линии → отказ.

## Источник
review-web641/WEB641-REVIEW-ACCESS-PLAN-20260913.md, BF-FIX-4; исходно вопрос Q28 экзаменаторов.
Связи
Лента
2026-09-13T09:55:55.218Z · coordinator
[13.09 09:55Z координатор] Взято в работу 13.09: волна 3644 (Codex Luna, A1) — preflight отката требует квитанцию «старое приложение прочитало новую схему» для конкретного пути отката; без квитанции откат не разрешается. Тесты: нет квитанции → отказ, есть → разрешено, квитанция от другой линии → отказ.
2026-09-13T10:21:09.456Z · coordinator
[13.09 10:21Z координатор] Закрыто волной 3644 (refs/waves/3644 = 2c8deeade): preflight отката теперь требует квитанцию «старое приложение прочитало новую схему» для конкретного заявленного пути отката; без квитанции этот откат не разрешается. Есть тесты на три ветки: нет квитанции → отказ, есть → разрешено, квитанция от другой линии → отказ. Смысл простой: откат перестал быть прыжком в темноту — прыгаем только туда, где заранее доказано, что старая версия умеет читать сегодняшнюю базу.
2026-09-14T23:54:55.551Z · coordinator
[14.09 23:54Z координатор] **Проверила, можно ли закрыть — НЕЛЬЗЯ, и объясняю разницу, потому что она тонкая.**

Тикет требует: **в preflight отката требовать квитанцию** «старое приложение прочитало новую схему» для конкретного заявленного пути отката; нет квитанции — откат не разрешается.

**Что сделано 15.09 и что это НЕ закрывает.** Я выполнила на живой базе read-only проверку формы данных:

```
Document.content IS NULL          0 из 3430
Document.userId   IS NULL       121 из 3430   (отдельная находка, WEB-664)
SourceTextChunk / ErasureTombstone / EmbedShareLinkAudience — по 0 строк
миграций применено 209 (записей 223, откачено 14, незавершённых 0)
```

Это **доказывает, что риск contract-миграции `web651` сегодня не материализован**: колонка стала nullable, но `NULL` нет ни в одной строке, значит старый код, читая `content`, получит значение. Сырьё — `расписки/compat-readback.txt` в пакете доступов.

**Но тикет просит не это.** Разница существенная:
- я проверила **форму данных** — снимок, верный на сейчас;
- тикет требует **квитанцию о поведении** («старое приложение прочитало») и, главное, **гейт**, который без неё откат не пустит.

Снимок формы данных гейтом не является: включат флаг новой ветки записи — `NULL` появятся, а снимок останется старым и будет врать.

**Что есть ещё, чего не было в тикете:**
- `migration-compatibility-l115n.json` — манифест объявляет по каждой миграции фазу (`expand`/`contract`), `rollbackSafe` и для `contract` — флаг со значением по умолчанию `off`;
- `migrations-l115n.py` **отказывается** сажать contract-миграцию, если её флаг не `off` по умолчанию или включён в окружении. То есть защита на **посадке** есть.
- ⚠️ **защиты на откате нет** — именно она и требуется этим тикетом.

**ОСТАТОК, уточнённый:**
1. добавить в preflight отката **обязательную** проверку квитанции совместимости для целевого релиза; нет квитанции — отказ, а не предупреждение;
2. определить, **чем квитанция выдаётся**: минимально — прогон старого артефакта против текущей схемы в одноразовом окружении (теперь для этого есть живая мишень — восстановленная копия базы, см. WEB-641);
3. связать с **WEB-661**: `verified.json` цели отката сейчас отсутствует у двух последних линий, так что у отката **уже** нет одного из своих предусловий.

Статус остаётся `review`. Закрывать нечего: сделана часть доказательства, а не требуемый гейт.
2026-09-15T02:35:02.761Z · coordinator
[15.09 02:35Z координатор] ## 15.09 05:40Z — волна 3958 сдала `VERDICT=GO`. Квитанция перед откатом есть, исполнимая, и доказана в обе стороны.

Ветка в зеркале: `refs/waves/3958/wave/3958-rollback-compat` = `ac016ddda40663c612e49f6450f2de477d4a456c`.

### Что сделано

`ops/line-factory/check-rollback-schema-compatibility.mjs` — читает файл схемы старого релиза и **фактический каталог живого PostgreSQL**, отвечает `YES`/`NO` кодом возврата. Не рассуждение, а команда.

**Таблица шести классов изменений — каждый проверен настоящим прогоном старого Prisma-клиента**, а не рассуждением:

| изменение после сборки старого клиента | что показал старый клиент | вердикт checker |
|---|---|---|
| новая таблица | модели нет, игнорирует | `SAFE extra-table` |
| новая колонка `NOT NULL` без default | чтение прошло, **`create` упал с `P2011`** | `DANGEROUS extra-required-column` |
| новая колонка `NULL` | чтение и запись прошли | `SAFE extra-nullable-column` |
| новая колонка `NOT NULL DEFAULT` | `create` прошёл, база подставила default | `SAFE extra-defaulted-column` |
| переименование колонки | запрос **упал с `P2022`** | `DANGEROUS rename-or-drop-column` |
| удаление колонки | запрос **упал с `P2022`** | `DANGEROUS missing-column` |

Область сравнения выбрана честно: модели, скалярные колонки, типы и enum. Relation-поля и `Unsupported(...)` не являются скалярным SQL-контрактом и в сравнение не входят.

### Дыра в инструменте посадки — закрыта

Канонический инструмент — `ops/line-factory/transition.py`. Теперь на стадии `rollback`, **до lock-файла и до изменения systemd-юнитов**, проверяются файл старой схемы и живая база; ненулевой результат останавливает переход (`transition.py:135`, `:143`, `:146`). Путь checker стал **обязательным полем манифеста**, проверяется `--check-files` и подставляется фабрикой в сгенерированный `transition.py` (`linefactory.py:79`, `:308`, `:609`). Старый JSON-гейт сохранён как дополнительное предусловие, но сам по себе живую проверку каталога не заменяет.

Проверка на реплике шла только на чтение: `noteclone` порт 55433, `recovery=true`, `transaction_read_only=on`, 219 публичных таблиц, 209 завершённых миграций.

### 🔴 KNOWN ISSUE волны, который я подтвердила независимо → **новый тикет WEB-667**

Волна заметила: в живой базе 209 применённых миграций, но пять их имён отсутствуют в обоих деревьях. Проверила сама на проде: **209 применено в базе, 204 каталога в релизе, пять применённых каталога не имеют**:

```
20260705000000_entity_resolution
20260704200000_economy_earn_receipt
20260704040000_daily_rollup
20260705_graph1126_name_embedding
20260705000000_wiki_1109_schema_claims
```

Обратного расхождения нет. Инструмент посадки этого не ловит: он сравнивает наборы **каталогов** между релизами, а не проверяет, что каждая применённая к базе миграция имеет каталог. Опасность: восстановление с нуля через миграции даст другую схему. Сегодняшнее доказательство восстановления (WEB-663) шло из базовой копии, а не через миграции, поэтому совпало и этой дыры не показало.

### Ещё одно, из KNOWN ISSUES волны

Положительная проверка на реплике выполнена отдельным read-only админским доступом с `--as-postgres`. Боевой путь `--unit` использует роль из web-юнита, и **при отсутствии прав на чтение каталога обязан остановиться с кодом 2, а не разрешить откат**. Это надо проверить на приёмке: иначе получится ещё одна объявленная защита, которая молчит.

### Остаток

Нужна независимая приёмка: главное — проверить, что при недостатке прав checker говорит «нет», а не «да».
2026-09-15T11:35:22.053Z · coordinator
[15.09 11:35Z координатор] [15.09 11:35Z координатор] Независимая приёмка 3984: VERDICT=GO.

На точной l115o кандидат ac016ddda40663c612e49f6450f2de477d4a456c проверен на disposable PostgreSQL 16.15: совместимая схема даёт READS_SCHEMA=YES; required NOT NULL без default, rename и drop дают NO; роль без прав на каталог даёт короткий ROLLBACK_SCHEMA_CHECK_ERROR/exit 2 без утечки credentials. Test double подтвердил, что отказ происходит до release lock и любого systemd-вызова. Focused checker 5/5, factory 152 pass/3 skip; evidence 5/5 SHA256.

Кандидат принят для следующей линии. WEB-653 остаётся review до посадки и post-QA на исполняемой линии; настоящий rollback и production не запускались.
2026-09-15T22:16:59.023Z · coordinator
ENRICH-4135-WEB-653

DRAFT_DELTA (append-only; правила обогащения: WEB-449, блок KNOWN ISSUES — канон
`web_issues.body id=WEB-449`, дамп `_extract/WEB-449.canon.txt` строки 85–86).
Волна 4135, M1/DeepSeek Flash 4.1, read-only аудит.
Источник: `/Users/poolpooly/waves-ds/4130/4130ENRICHBUSFACTOR-REPORT.md` + `_extract/`.

## 1. UTC / КТО / ВЕРДИКТ и точный статус

Тело датировано 13.09 (`updated=2026-09-15T02:35:03.517Z`) и вердикта не несёт. Живой
вердикт — комментарий `2026-09-15T11:35:22.053Z` (координатор): независимая приёмка 3984
**`VERDICT=GO`**; кандидат `ac016ddda40663c612e49f6450f2de477d4a456c` проверен на
disposable PostgreSQL 16.15: совместимая схема даёт `READS_SCHEMA=YES`; required `NOT NULL`
без default, rename и drop дают `NO`; роль без прав на каталог даёт короткий
`ROLLBACK_SCHEMA_CHECK_ERROR`/exit 2 без утечки credentials; focused checker 5/5,
factory 152 pass/3 skip, evidence 5/5 SHA256.

Точный статус в живой доске: **`review`** (5 комментариев, последний —
`2026-09-15T11:35:22.053Z`). Формулировка закрытия дословна: «WEB-653 остаётся review
до посадки и post-QA на исполняемой линии; настоящий rollback и production не запускались».

## 2. МЕХАНИЗМ / КОРЕНЬ

Корень (тело, «Суть»): книга разрешает откат на предыдущий релиз, но **нигде не требует
доказательства, что ТОТ релиз умеет читать схему БД, которая уже мигрировала вперёд**.
Если не умеет — «откат превращается в аварию вместо спасения».

**Устройство исправления**, добытое 15.09 (не в теле): `ops/line-factory/check-rollback-schema-compatibility.mjs`
читает файл схемы старого релиза и **фактический каталог живого PostgreSQL**, отвечает
`YES`/`NO` кодом возврата — «не рассуждение, а команда» (`2026-09-15T02:35:02.761Z`).
Таблица шести классов изменений проверена **настоящим прогоном старого Prisma-клиента**:
новая таблица → `SAFE extra-table`; новая колонка `NOT NULL` без default → чтение прошло,
**`create` упал с `P2011`** → `DANGEROUS extra-required-column`; новая колонка `NULL` →
чтение и запись прошли → `SAFE extra-nullable-column`; новая колонка `NOT NULL DEFAULT` →
`create` прошёл, база подставила default → `SAFE extra-defaulted-column`; переименование
колонки → **`P2022`** → `DANGEROUS rename-or-drop-column`; удаление колонки → **`P2022`** →
`DANGEROUS missing-column`. Область сравнения названа честно: модели, скалярные колонки,
типы и enum; relation-поля и `Unsupported(...)` в скалярный SQL-контракт не входят.

Дыра в инструменте посадки закрыта: канонический `ops/line-factory/transition.py` на стадии
`rollback`, **до lock-файла и до изменения systemd-юнитов**, проверяет файл старой схемы
и живую базу; ненулевой результат останавливает переход (`transition.py:135`, `:143`,
`:146`). Путь checker стал **обязательным полем манифеста**, проверяется `--check-files`
и подставляется фабрикой в сгенерированный `transition.py` (`linefactory.py:79`, `:308`,
`:609`). Старый JSON-гейт сохранён как дополнительное предусловие, но сам по себе живую
проверку каталога не заменяет.

Правило одной папки: `2026-09-14T20:49:32.326Z` — «мне нужна одна единственная папка,
а не куча мест».

## 3. КАРТА ДОКУМЕНТОВ (точные пути)

- Внешний разбор: `review-web641/WEB641-REVIEW-ACCESS-PLAN-20260913.md`, BF-FIX-4;
  исходно вопрос **Q28** экзаменаторов
- Ветка в зеркале: `refs/waves/3958/wave/3958-rollback-compat` =
  `ac016ddda40663c612e49f6450f2de477d4a456c`
- Checker: `ops/line-factory/check-rollback-schema-compatibility.mjs`
- Инструмент посадки: `ops/line-factory/transition.py`; фабрика `ops/line-factory/linefactory.py`
- Манифест совместимости: `migration-compatibility-l115n.json` — по каждой миграции фаза
  (`expand`/`contract`), `rollbackSafe`, для `contract` — флаг со значением по умолчанию `off`
  (`2026-09-14T23:54:55.551Z`)
- Скрипт миграций: `migrations-l115n.py` — **отказывается** сажать contract-миграцию, если
  её флаг не `off` по умолчанию или включён в окружении (там же)
- Сырьё проверки формы данных: `расписки/compat-readback.txt` в пакете доступов (там же)
- Отчёт независимой приёмки: `3984` (`2026-09-15T11:35:22.053Z`)
- Отчёт этой волны: `/Users/poolpooly/waves-ds/4135/4135ENRICHBUSFACTORCOMMENTS-REPORT.md`

## 4. ЭВОЛЮЦИЯ и отозванные утверждения

1. **Преждевременное закрытие отозвано.** Комментарий `2026-09-13T10:21:09.456Z`:
   «Закрыто волной 3644 (`refs/waves/3644` = `2c8deeade`)… откат перестал быть прыжком
   в темноту». Комментарий `2026-09-14T23:54:55.551Z` отзывает это: «**Проверила, можно ли
   закрыть — НЕЛЬЗЯ**, и объясняю разницу, потому что она тонкая».
2. **Причина отзыва сформулирована точно и обязана сохраниться:** выполнена read-only
   проверка **формы данных** на живой базе — `Document.content IS NULL` **0 из 3430**,
   `Document.userId IS NULL` **121 из 3430** (отдельная находка, **WEB-664**),
   `SourceTextChunk`/`ErasureTombstone`/`EmbedShareLinkAudience` — по 0 строк, миграций
   применено **209** (записей 223, откачено 14, незавершённых 0). Это доказывает, что риск
   contract-миграции `web651` **сегодня не материализован**. Но: «я проверила **форму
   данных** — снимок, верный на сейчас; тикет требует **квитанцию о поведении**… и, главное,
   **гейт**, который без неё откат не пустит. Снимок формы данных гейтом не является: включат
   флаг новой ветки записи — `NULL` появятся, а снимок останется старым и будет врать».
3. **Асимметрия защиты, найденная 14.09:** `migrations-l115n.py` **отказывается** сажать
   contract-миграцию с небезопасным флагом — «защита на **посадке** есть»; «⚠️ **защиты
   на откате нет** — именно она и требуется этим тикетом» (`2026-09-14T23:54:55.551Z`).
4. **Связь с WEB-661:** `verified.json` цели отката отсутствует у двух последних линий,
   «так что у отката **уже** нет одного из своих предусловий» (там же).
5. **Новый KNOWN ISSUE волны 3958 → WEB-667.** В живой базе **209 применённых миграций,
   но пять их имён отсутствуют в обоих деревьях**: `20260705000000_entity_resolution`,
   `20260704200000_economy_earn_receipt`, `20260704040000_daily_rollup`,
   `20260705_graph1126_name_embedding`, `20260705000000_wiki_1109_schema_claims`
   (209 применено в базе, 204 каталога в релизе; обратного расхождения нет). Инструмент
   посадки этого не ловит: он сравнивает наборы **каталогов** между релизами, а не проверяет,
   что каждая применённая к базе миграция имеет каталог. Опасность: восстановление с нуля
   через миграции даст другую схему; доказательство восстановления WEB-663 шло из базовой
   копии, а не через миграции, поэтому совпало и этой дыры не показало
   (`2026-09-15T02:35:02.761Z`).
6. **Готовность пакета ≠ исполнение B/C.** `HANDOFF-LIVE.md` строка 27 (тик 15.09 21:28Z);
   WEB-649 `2026-09-15T15:47:17.420Z`.

## 5. KNOWN ISSUES / ТРАБЛШУТИНГ

Блока в теле нет; проверки за 2 минуты нет ни в теле, ни в комментариях.

**KI-1. Защита, которая молчит, когда нет прав.**
- Симптом: положительная проверка на реплике выполнена отдельным read-only админским
  доступом с `--as-postgres`; боевой путь `--unit` использует роль из web-юнита, и **при
  отсутствии прав на чтение каталога обязан остановиться с кодом 2, а не разрешить откат**.
  Иначе «получится ещё одна объявленная защита, которая молчит» (`2026-09-15T02:35:02.761Z`).
- **Проверка за 2 минуты (read-only):** запустить checker ролью без прав на чтение каталога
  и посмотреть код возврата. Ожидание: **`ROLLBACK_SCHEMA_CHECK_ERROR`/exit 2** без утечки
  credentials (`2026-09-15T11:35:22.053Z`). Exit 0 ⇒ защита декоративная.
- Причина: положительная проверка шла под ролью с расширенными правами.
- Лечение/статус: независимо принято 3984 (роль без прав даёт exit 2; test double подтвердил,
  что отказ происходит **до release lock** и любого systemd-вызова).
- Ссылки: `4130ENRICHBUSFACTOR-REPORT.md` §4 WEB-653.

**KI-2. Применённые миграции без каталогов не ловятся инструментом посадки.**
- Симптом: 209 применено в базе, 204 каталога в релизе; пять имён применённых миграций
  отсутствуют в обоих деревьях — восстановление с нуля через миграции даст другую схему
  (`2026-09-15T02:35:02.761Z`; отдельный тикет **WEB-667**).
- **Проверка за 2 минуты (read-only):** сверить число завершённых миграций в живой базе
  с числом каталогов миграций в релизе. Расхождение ⇒ есть применённые миграции без каталога.
  (На реплике контрольные числа: `noteclone` порт 55433, `recovery=true`,
  `transaction_read_only=on`, **219 публичных таблиц, 209 завершённых миграций** — там же.)
- Причина: инструмент посадки сравнивает наборы **каталогов** между релизами, а не факт
  «каждая применённая миграция имеет каталог».
- Лечение/статус: OPEN, вынесено в **WEB-667**; выдумывать ничего не стали — «ничего не
  выдумано» (`2026-09-15T15:40:18.235Z`).
- Ссылки: `4130ENRICHBUSFACTOR-REPORT.md` §4 WEB-653.

## 6. ТОЧНЫЙ ОСТАТОК (граница владелец / исполнитель)

Тело остатка не содержит; точный остаток — комментарий `2026-09-15T11:35:22.053Z`
(«WEB-653 остаётся review до посадки и post-QA на исполняемой линии») плюс уточнение
`2026-09-14T23:54:55.551Z`.

- **Посадка кандидата `ac016ddda40663c612e49f6450f2de477d4a456c` в следующую линию** —
  **исполнитель** (WEB-641 `2026-09-15T13:05:49.045Z` пункт 3).
- **Post-QA исполняемого гейта на посаженной линии; настоящий rollback и production
  не запускались** — **исполнитель** (там же, пункт 4).
- **Квитанция совместимости как обязательное предусловие отката**: нет квитанции — отказ,
  а не предупреждение; определить, чем квитанция выдаётся — минимально прогон старого
  артефакта против текущей схемы в одноразовом окружении; для этого есть живая мишень —
  восстановленная копия базы (`2026-09-14T23:54:55.551Z`). Исполнитель — **координатор +
  владелец БД** (WEB-649 §5 пункт 4).
- **Связь с WEB-661**: `verified.json` цели отката отсутствует у двух последних линий —
  у отката нет одного из предусловий (там же).

## 7. ПЕРВЫЙ ШАГ НУЛЕВОГО АГЕНТА (без агентов)

Прочитать `2026-09-14T23:54:55.551Z` (разбор, почему 13.09 закрыть было нельзя) и
`2026-09-15T11:35:22.053Z` (вердикт) — тело не несёт ни того, ни другого. Затем выполнить
проверку KI-1 (checker ролью без прав на каталог, ожидание exit 2). **Не запускать настоящий
rollback и не закрывать:** `review`, производственный прогон не выполнялся; без квитанции
совместимости откат — авария, а не спасение.
2026-09-15T23:42:37.997Z · coordinator
L115Q-LANDING-20260915-WEB-653
[15.09 23:38Z координатор] Новая исполняемая линия после предыдущего WAIT/REVIEW действительно посажена.

- Прод: `l115q`, exact source `cc278e1810e1e03ac277b8e3a5fe06a2aebfba30`, artifact `e9cdd5b041c29d058ef3c06e11ac39862e4de57d4ea307e79a7902d2d6535d86`.
- Machine verify: 3010/3012 `ready=true`, `paidReady=true`, `posture=enforce_ready`; background fresh; 4 unit checks success; old-process count 0; оба worker timer active.
- Миграции: live ledger 211, pending 0, inherited no-op receipt; rollback compatibility old l115o → новая схема: `dangerousCount=0`.
- Общий post-landing: BF-08 PASS по 107 changed paths / 105 operational paths / 8 классам. Browser/editor вошёл, увидел 58 документов, открыл документ; новых ошибок после клика 0.
- Платный smoke: один HTTP 200; одна бронь `SETTLED_ACTUAL` 40 724→1 013 микро; один terminal event `APPLIED`; artifact SHA совпадает.
- Evidence: `nc-ops-scripts/release-l115q/postlanding-evidence/`, SHA-манифест проверен.

Важно: эта общая расписка снимает только ожидание новой линии и общий landing/browser/paid smoke. Статус этой карточки не меняется автоматически: `done` допустим только после её собственного узкого served-boundary/post-QA критерия из последнего комментария. Следующий шаг нулевого агента — выполнить именно этот узкий остаток на l115q и записать отдельную машинную расписку; старые l115o FAIL/WAIT не считать текущим состоянием линии.
2026-09-16T03:11:52.846Z · coordinator
COORD-POSTQA-20260916-WEB653-0307Z

Post-QA 4162: VERDICT=FAIL/UNVERIFIED, карточку не закрывать. Сам gate на доступных ветвях fail-closed, но в доставленном exact-l115q архиве нет line manifest, landing receipt с dangerousCount=0 и verified.json; served-line Task B не выполнен. Дополнительно rollback читает verified.json безусловно и при его отсутствии падает до assertion; gate dangerousCount как поле не проверяет; wall-clock freshness bound отсутствует. Откат не запускался. Evidence: /Users/poolpooly/waves-ds/4162/{report.md,evidence,SHA256SUMS,VERDICT-FAIL}.
2026-09-16T05:31:23.023Z · coordinator
POSTQA-L115Q-4162-WEB653-INCOMPLETE

DeepSeek Flash post-QA 4162 завершён с `FAIL — evidence incomplete`; WEB-653 остаётся `review`.

- сам rollback-compatibility gate исправен: 8/8 ветвей отказа дали ненулевой код с явным сообщением, positive controls прошли;
- served-line binding не доказан: в материале отсутствовали manifest линии `l115q` и landing receipt с `dangerousCount=0`;
- отдельно отсутствует `verified.json` rollback-target, поэтому даже зелёный compatibility gate не разрешает откат;
- `SHA256SUMS` SHA-256 `cf4f7a69f2f99c12dd79ebe9ab678b1808e12e85ccc05d4caf940dc10f29aeb2`, readback PASS;
- report SHA-256 `e53270a2cb71823436c1fe978fd46e5cf85ca499d1f69b064f73a4e0176307ac`.

Следующий шаг: доставить exact l115q line manifest, landing receipt/path+SHA с `dangerousCount=0` и rollback-target `verified.json`, затем повторить только served-line binding. Откат не выполнялся; переход в `done` не разрешён.
2026-09-16T06:14:20.509Z · coordinator
{marker}

WEB-653 served-boundary post-QA is PASS/GO on live l115q.

- served source cc278e1810e1e03ac277b8e3a5fe06a2aebfba30, artifact e9cdd5b041c29d058ef3c06e11ac39862e4de57d4ea307e79a7902d2d6535d86;
- canonical line manifest is hash-bound; rollback-target l115o verified.json source/artifact match;
- landing receipt dangerousCount=0 and output/readback SHA binding match;
- 34/34 live predicates PASS; exact landed gate dry-run: 2 positive permits, 10 negative branches refuse before systemd mutation; missing/mismatched target receipt refuses; the live checker re-derives dangerous.length===0;
- secret scan 13 files / 1252 lines: 0 findings; actual SHA lines 17/17 match. The author SHA256SUMS contains comments, so stock shasum -c exits 1; this is manifest formatting, not a content mismatch, and the coordinator verified all actual hash lines independently.

Report SHA-256 61ea8a9a6d9206ae412dffa29c4371645c968eb629b24a5d9ead8b3ed3cad03c; evidence manifest d383b8692d265ddafbed6ecac0a9cf11500c4b5032abac00f6bfbbc28161bac3. No actual rollback was executed: the pre-mutation decision path was tested.

The new P1 host-factory reproducibility finding is tracked separately in {newid} and is not hidden by closing this card. WEB-653 acceptance criterion is met; done is authorized.
2026-09-16T06:14:49.480Z · coordinator
POSTQA-4187-WEB653-PASS

Correction to the immediately preceding comment: its first line rendered the literal placeholder {marker}. The complete evidence and decision below that line are unchanged. WEB-653 served-boundary post-QA is PASS; follow-up factory reproducibility defect is WEB-673.
Воркер
не привязан — привязать: curl -X POST https://bugs.wool2.online/api/web/assign -H 'content-type: application/json' \ -d '{"issueId":"WEB-653","session":"<имя tmux-сессии>","host":"m4"}'
Обновлён
2026-09-16T06:14:20.510Z