Задания, отчёты и патчи, лежавшие в C:\Users\Ochenstarik\projects и в домашней папке, перенесены в agents/. Разложено по агентам там, где имя файла позволяло определить автора; остальное — в _salvage-2026-08-18/ и разбирается вручную. Патчи в notes/salvage-2026-08-18/ — незакоммиченная работа из брошенных рабочих копий: она существовала только на диске. Тяжёлое (релизные архивы, инсталляторы, наборы данных) в репозиторий не попало: оно лежит рядом, в Agent_projects/_archive и Agent_projects/_data. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
10 KiB
Воспроизводимость сборки: централизованные версии пакетов и lock-файлы
Часть 1 — сначала исправить PR #14
Не начинать новую ветку, пока не закрыты два дефекта в уже открытом PR #14 (antigravity/repo-hygiene). Оба находятся в release-workflow, которые не запускаются на pull request — они триггерятся только по тегу и workflow_dispatch, поэтому зелёный CI на PR их не покрывает.
1.1. permissions стоит на уровне шага — это недопустимый ключ
Три места: linux-release.yml:63, linux-release.yml:127, windows-release.yml:86.
- name: Attach bootstrap to GitHub Release
uses: softprops/action-gh-release@3bb1273…
permissions: # ← уровень шага
contents: write
with:
В схеме GitHub Actions permissions существует только на уровне workflow и на уровне job. У шага такого ключа нет. При срабатывании по тегу workflow либо не загрузится с ошибкой Unexpected value 'permissions', либо шаг выполнится с унаследованным contents: read и публикация релиза упадёт с 403.
Исправление: перенести permissions: contents: write на уровень job. В linux-release.yml это оба job — bootstrap и publish; в windows-release.yml — package. Workflow-уровень оставить contents: read.
Job-уровень — самая мелкая гранулярность, которую даёт GitHub; более узко не бывает.
1.2. SBOM генерируется и выбрасывается
Три места: linux-release.yml:57, linux-release.yml:111, windows-release.yml:67.
Файл SBOM не попадает ни в path: шага upload-artifact, ни в files: шага action-gh-release. То есть он создаётся и остаётся в рабочем каталоге runner'а. Требование задания «приложить к артефактам релиза» не выполнено.
Сопутствующее:
|| trueгасит любую ошибку генерации: сломанный шаг выглядит зелёным, а SBOM молча отсутствует. Для артефакта цепочки поставки это обесценивает саму затею — убрать;- в job
bootstrapфайлаlinux-release.ymlнет шагаsetup-dotnet, а SBOM пишется в подкаталогsbom/, тогда как остальные пути в этом job — корневые; - SBOM в job
bootstrapи в jobpublishстроится из одного и того жеServerMonitorManager.slnx, то есть дублируется.
Исправление: генерировать SBOM один раз, до шага загрузки, в job, где уже настроен .NET; добавить файл и в upload-artifact, и в files: релиза; || true убрать. Дублирующую генерацию в job bootstrap удалить.
1.3. Как проверить себя
Release-workflow нельзя прогнать через PR. Проверять так:
actionlintна все пять файлов.github/workflows/— он ловит и недопустимые ключи, и опечатки в схеме;- либо
gh workflow runвручную поworkflow_dispatchна своей ветке, если это не приведёт к публикации релиза (шаги релиза защищеныif: startsWith(github.ref, 'refs/tags/'), поэтому на ветке они пропускаются, а разбор файла всё равно произойдёт).
В отчёте показать вывод проверки. Утверждение «исправлено» без демонстрации разбора файла не принимается — именно отсутствие такой проверки и привело к дефекту.
Часть 2 — новая задача
Репозиторий и ветка
- Репозиторий: https://github.com/ochenstarik-ui/server-monitor-manager
- База:
main(после merge PR #13 и #14) - Ветка:
antigravity/reproducible-builds - Рабочая папка:
C:\Users\Ochenstarik\projects\smm-antigravity
Зачем
docs/installer-contract.md §1 требует, чтобы production-установка использовала закреплённый релиз, а docs/product-horizons.md, Горизонт 0, требует подписанной поставки с хэшами всех артефактов. Обе гарантии опираются на предположение, что из одного коммита получается один и тот же бинарник.
Сейчас это неверно: версии NuGet-пакетов заданы в каждом .csproj по отдельности, lock-файлов нет, восстановление зависимостей идёт по диапазонам. Сборка одного и того же тега завтра может дать другой набор зависимостей, и никакая подпись артефакта этого не покажет.
Задача закрывает предпосылку, без которой подписанная поставка не имеет смысла.
Работы
1. Directory.Packages.props в корне.
Централизованное управление версиями: ManagePackageVersionsCentrally включено, все PackageVersion собраны в одном файле, из .csproj версии убраны, остаются только PackageReference без атрибута Version.
Затрагиваются все проекты решения: Core, Control, Agent, Desktop, Provisioning.Helper, тестовый проект.
2. Lock-файлы.
RestorePackagesWithLockFile включён, packages.lock.json каждого проекта закоммичен. В CI восстановление выполняется с --locked-mode, чтобы расхождение lock-файла с проектом ломало сборку, а не молча подтягивало другую версию.
3. Directory.Build.props в корне.
Nullable,EnableNETAnalyzers,AnalysisLevel latest-recommended;InvariantGlobalizationтам, где это уместно для серверных проектов;- детерминированная сборка:
Deterministic,ContinuousIntegrationBuildпод условием$(CI); TreatWarningsAsErrorsне включать — см. границы.
4. CI.
В linux-control-agent.yml и windows-build.yml добавить восстановление в locked-режиме. Прогон должен падать, если lock-файл не соответствует проектам.
Границы
Не изменять:
src/ServerMonitorManager.Control/LinkService.cs,LinkPolicyApplier.cs,LinkReconciliationBackgroundService.cs,ControlStore.cs,ControlMaintenance.cs,ControlOptions.cs,appsettings.json;src/ServerMonitorManager.Provisioning.Helper/**,deploy/**;CertificateAuthority.cs,CertificateLifecycleService.cs,src/ServerMonitorManager.Agent/AgentClient.cs;- содержимое файлов в
tests/**— правки допускаются только в.csprojтестового проекта.
Первые две группы заняты заданием B-3R, третья — заданием по жизненному циклу сертификатов.
Не входит в задание:
TreatWarningsAsErrors. Включение сейчас сломает незавершённую веткуhermes/task3-b3-fact-reconciliationпри её ребейзе на main. Отдельная задача после её merge;- разделение тестов на
Core.Tests/Control.Tests/Agent.Tests/Helper.Tests— тестовые файлы заняты; - обновление версий пакетов: задача переносит существующие версии как есть, а не апгрейдит их. Любое изменение номера версии должно быть отдельно обосновано в отчёте;
- merge собственного PR.
Критерий приёмки
- ни один
.csprojне содержитVersion=вPackageReference; packages.lock.jsonзакоммичены для всех проектов;- CI восстанавливает зависимости в
--locked-modeи падает при рассинхроне — продемонстрировать намеренной поломкой lock-файла в отдельном временном коммите, показать красный прогон, затем откатить; - номера версий пакетов не изменились относительно
main— показать сравнение; git diff --name-only mainне содержит ни одного файла из списка границ;- Control suite прогнан на Linux;
- Desktop собирается — в CI, локально WinUI 3 собрать нельзя, если нет Windows App SDK;
- CI зелёный, PR создан и не смержен.
Отчётность
Раздельно: что проверено локально, что в CI со ссылками на прогоны, что не проверялось и почему.
Отдельно показать: вывод actionlint по части 1 и сравнение версий пакетов до и после по части 2.
Физический acceptance (SMM_ACCEPT_RESTORE=1 SMM_ACCEPT_REBOOT=1 tests/acceptance/three-server-mesh.sh) выполнить нельзя — не выданы SSH- и topology-параметры.