Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
- Date: `2026-08-12`
- Status: `approved`
- Decision: `none` — no architectural contract changed

# Windows-контракт публикации внешних артефактов v8-runner

## Контекст

Unica 0.11.0 поставляет `v8-runner` 0.5.1 из commit
`72d346c0a8fcf8373d9388257d11e6bef0ad70b2`. На Windows команда `make`
успешно формирует и проверяет staged EPF/ERF, но затем может завершиться кодом
3 при публикации каталога `output`: старый runner открывает родительский каталог
как обычный файл перед `fsync`, получает `ERROR_ACCESS_DENIED` или
`ERROR_PATH_NOT_FOUND` и запускает rollback. Это пользовательский дефект
из #310, а его EPF-воспроизведение — #264.

Корневая причина уже исправлена в `alkoleft/v8-runner-rust#48`: вне Unix
directory fsync становится успешной no-op, а ошибки создания, записи и rename
не подавляются. Unica PR #419 обновил lock и бинарные assets до upstream commit
`7ce1b062843d86644fe55741dbe0ee79f7ca767d`, содержащего исправление. Однако
consumer-level контракт Unica проверяет запуск внешнего EPF, но не выполняет
`v8-runner make` до финальной публикации каталога. Поэтому поставка может снова
закрепить старый или регрессировавший бинарник, не получив падающего CI.

## Цель

Добавить воспроизводимый Windows-контракт для поставляемого `v8-runner`, который
проходит весь путь `make` для внешней обработки до публикации output-каталога и
отличает исправленный бинарник от поставленного в Unica 0.11.0.

## Выбранный подход

Расширить `scripts/ci/check-tool-contracts.py` отдельной проверкой Windows
external publication. Проверка запускает настоящий бинарник из проверяемого
runtime, но заменяет платформу 1С небольшим Rust-stub, собранным во временном
каталоге. Stub реализует только наблюдаемые Designer batch-вызовы, нужные
`make`:

1. при `/LoadExternalDataProcessorOrReportFromFiles <root-xml> <binary>` пишет
детерминированный непустой staged EPF;
2. при `/DumpExternalDataProcessorOrReportToFiles <root-xml> <binary>` пишет
минимальный XML с тем же именем объекта, чтобы runner подтвердил артефакт;
3. при `/Out <log>` создаёт ожидаемый журнал и завершает вызов кодом 0.

Временный `v8project.yaml` использует `format: DESIGNER`,
`builder: DESIGNER`, source-set `EXTERNAL_DATA_PROCESSORS`, явный локальный
`tools.platform.path` к stub и файловую строку подключения. Исходники содержат
один минимальный descriptor внешней обработки. Реальная платформа, лицензия и
пользовательская информационная база не нужны.

Проверка выполняется только для target `win-x64`, потому что дефект зависит от
Windows directory-handle semantics. Она запускает два последовательных `make`:

- первый публикует staged-каталог в отсутствующий относительный output;
- второй заменяет уже существующий output через backup/rollback boundary.

После каждого запуска проверка требует код 0, валидный JSON-envelope, заявленный
EPF в artifacts, существующий непустой файл в output и отсутствие принадлежащих
этому запуску `.artifacts-stage-*`, `.artifacts-backup-*` и их `.meta.json`
sidecar. Второй запуск также доказывает, что старый набор EPF в target
действительно заменён.

Текущий locked asset дополнительно переносит `Alpha.epf.meta.json` внутрь
опубликованного output. Это предсуществующее кроссплатформенное поведение
upstream staging-публикатора, а не Windows-fsync дефект #310. Контракт не
закрепляет этот sidecar как обязательный и не втягивает его исправление в этот
PR: он требует ровно один опубликованный EPF и отдельно запрещает временные
`.artifacts-*` пути и sidecar.

## Воспроизведение и доказательство исправления

До изменения тест запускается вручную тем же helper-кодом против двух
immutable бинарников:

- runtime Unica 0.11.0, source commit `72d346c0...`: ожидается устойчивый отказ
на Windows directory fsync после успешного platform-stub шага;
- текущий lock Unica, source commit `7ce1b062...`: ожидаются обе успешные
публикации и полная очистка временных единиц.

В PR фиксируются команды, exit codes и существенная диагностика обоих запусков.
Старый бинарник и его байты в репозиторий не добавляются.

## Рассмотренные альтернативы

### Новый upstream fix

Отклонён: production-исправление и Windows unit regression уже слиты в
`v8-runner-rust#48`. Новый PR дублировал бы существующее изменение и не защищал
бы consumer lock Unica.

### Live smoke с установленной платформой 1С

Отклонён для обязательного CI: тест зависел бы от закрытой платформы, лицензии,
версии установки и состояния информационной базы. Локальное live-воспроизведение
может дополнять PR evidence, но не заменяет детерминированный contract test.

### Только проверка commit/hash в tools.lock

Отклонена: provenance доказывает происхождение байтов, но не пользовательское
поведение `make`. Нужен исполняемый контракт финальной публикации.

## Ошибки и границы

- Проверка не подавляет ошибки platform-stub, JSON-протокола, staging, rename,
публикации или очистки; каждая возвращается с префиксом
`v8-runner Windows external publication contract`.
- Skip разрешён только для target, отличного от `win-x64`; отсутствие runner,
`rustc` или ожидаемого результата на Windows является failure.
- Проверка не меняет CLI/MCP surface, tool lock, runtime budgets или политику
staged publication. Она закрепляет уже поставленное исправление.

## Проверка

1. Новый unit-тест Python проверяет формирование fixture, разбор envelope и
диагностические ошибки helper-а без запуска внешнего процесса там, где это
можно изолировать.
2. Windows contract запускается против runtime 0.11.0 и обязан воспроизвести
исходный отказ до принятия текущего locked binary как baseline.
3. Та же проверка проходит против текущего `win-x64` asset.
4. `tests/ci/test_product_contracts.py`, `check-tool-contracts.py` для Windows,
`git diff --check` и обязательный `Unica CI` проходят.

## Архитектурное влияние

Новая ADR не требуется. Публичные инструменты, аргументы, результаты,
идентичность MCP-сервера, владение runtime и контракт упаковки не меняются.
Проверка лишь добавляет исполняемое доказательство уже принятого и поставленного
Windows-поведения внешнего `v8-runner`.
Loading