Задания, отчёты и патчи, лежавшие в 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>
180 lines
20 KiB
Markdown
180 lines
20 KiB
Markdown
# План доделывания Server Monitor Manager — для облачного исполнителя (Gemini)
|
||
|
||
Составлен 2026-08-04. Заменяет собой полосовое разделение: Codex приостановлен, Gemini ведёт весь объём.
|
||
|
||
Базовая ревизия на GitHub: `main` @ `b11c277`.
|
||
|
||
---
|
||
|
||
## 0. Передача работ — сделать до постановки первой задачи
|
||
|
||
### 0.1. B-3R существует только локально
|
||
|
||
Незавершённая работа по блоку B-3R — **24 изменённых файла, около 1276 добавленных строк** — лежит незакоммиченной в `C:\Users\Ochenstarik\projects\server-monitor-manager-task3-b3`. На GitHub этой ветки нет: последняя ветка серии на remote — `hermes/task2-b-link-reconciliation` (`1580ec7`), это ещё блок B-2.
|
||
|
||
Облачный исполнитель этого не видит. Возможные решения:
|
||
|
||
| Вариант | Последствия |
|
||
|---|---|
|
||
| **Закоммитить и запушить** ветку `hermes/task3-b3-fact-reconciliation` как есть, с честной пометкой «незавершено, см. B3R_SPEC» | Работа сохранена, Gemini продолжает с неё. Рекомендуемый вариант |
|
||
| Выгрузить патчем и передать файлом | Работает, но патч быстро протухнет и его придётся накатывать вручную |
|
||
| Отбросить и переделать с `b11c277` | Потеря корректной работы: R3 закрыт полностью, R1 закрыт на уровне отдельной мутации — я это проверял по коду |
|
||
|
||
Пока решение не принято, ставить задачу по B-3R облачному исполнителю нельзя: он либо не найдёт контекст, либо начнёт вторую параллельную реализацию.
|
||
|
||
### 0.2. Нормативные документы тоже локальны
|
||
|
||
Коммит `4de849e` (`docs/product-horizons.md`, `docs/approval-policies.md`, `docs/integration-kagent.md`, правки `roadmap.md` и `security-model.md`) находится на локальной ветке `docs/product-horizons-and-integration` и не запушен. Пока он не в репозитории, план ниже ссылается на документы, которых исполнитель не увидит.
|
||
|
||
Запушить до выдачи первой задачи.
|
||
|
||
---
|
||
|
||
## 1. Что облачный исполнитель проверить не может
|
||
|
||
Это самый важный раздел. Дисциплина честной отчётности была сильной стороной предыдущих пакетов — её надо сохранить, а список ограничений изменился.
|
||
|
||
| Проверка | Доступно в облаке | Что делать |
|
||
|---|---|---|
|
||
| Control test suite на Linux | **Да** | Запускать обязательно. Именно Linux-прогон в блоке B-3 выявил, что integration-тест остался на старом протоколе: на Windows он молча пропускался по `OperatingSystem.IsLinux()` |
|
||
| Тесты helper, nftables, bash-контракты | **Да** | Запускать; это естественная среда для них |
|
||
| Сборка Desktop (WinUI 3) | **Нет** | Требует Windows и Windows App SDK. Полагаться на `windows-build.yml` в CI. **Никогда не заявлять локальную сборку Desktop** |
|
||
| `Test-DesktopContracts.ps1` | Частично | Это сопоставление строк в XAML, идёт под `pwsh` на Linux. Указывать, что запущено под pwsh, а не на Windows |
|
||
| Физический acceptance | **Нет ни у кого** | Требует `HUB_SSH_HOST`, `HUB_SSH_USER`, `SOURCE_SSH_HOST`, `SOURCE_SSH_USER`, `HOME_WG_IP`, `SECOND_WG_IP`, `SSH_IDENTITY_FILE`. Не выдавать mock- и contract-тесты за него |
|
||
| Trimmed linux-x64 publish | **Да** | Полезно: проверка source-generated сериализации на реальном опубликованном артефакте |
|
||
|
||
**Правило.** В `TEST_EVIDENCE.md` раздельно перечисляются: что запущено локально, что запущено в CI, что не запускалось и почему. Отсутствие проверки — не дефект отчёта; выдача непроверенного за проверенное — дефект.
|
||
|
||
---
|
||
|
||
## 2. Формат сдачи для облака
|
||
|
||
Прежние пакеты складывались в локальную папку. Для облачного исполнителя:
|
||
|
||
- **Основной артефакт — Pull Request.** Якорь целостности — SHA коммита, а не `SHA256SUMS`. В отчёте указывать точный SHA, который ревьюился, и SHA, который смержен, если они различаются.
|
||
- `REPORT.md` и `TEST_EVIDENCE.md` — в описании PR либо отдельным коммитом в PR; содержание прежнее.
|
||
- `CI_CHECKS.txt` заменяется ссылкой на прогон checks в PR.
|
||
- `INDEPENDENT_REVIEW.md` — обязателен по-прежнему, и **ревьюеру передаётся полный текст задачи**, а не только диф. Три блока из четырёх дали находки класса «пропущенное требование», которые при diff-only ревью не ищутся вовсе.
|
||
- Один PR — одна тема. Смешанные PR не принимаются.
|
||
- Статус после merge — `merged / verified`, а не «выполнено», если критерий сформулирован через фактическое поведение и не измерялся на реальной топологии.
|
||
|
||
---
|
||
|
||
## 3. Первая задача — калибровочная
|
||
|
||
Дешёвая, быстрая, с нулевым риском для существующего кода, и результат проверяется объективно. Даёт понять, справляется ли исполнитель, до того как ему доверят control plane.
|
||
|
||
### Задача A — гигиена репозитория и цепочки поставки · до одного дня
|
||
|
||
Только новые файлы и `.github/**`. Ни одного файла, который трогает ветка B-3R.
|
||
|
||
1. Запинить все GitHub Actions по commit SHA. Сейчас плавающие `@v6`, `@v5`, `@v2` — содержимое шага может измениться без нашего ведома.
|
||
2. `.github/dependabot.yml` для `github-actions` и `nuget`.
|
||
3. Сузить `permissions: contents: write` в `linux-release.yml` и `windows-release.yml` до job'а публикации.
|
||
4. `SECURITY.md` — куда сообщать об уязвимости, ожидаемое время ответа, что считается уязвимостью. Для инструмента безопасности его отсутствие — само по себе дефект.
|
||
5. `CHANGELOG.md` в формате Keep a Changelog, заполнить по существующим тегам `v0.1.0-alpha.1…5`.
|
||
6. `CONTRIBUTING.md`, `CODEOWNERS`, шаблоны issue и PR.
|
||
7. SBOM (`dotnet CycloneDX`) как артефакт релиза.
|
||
8. Удалить слитые ветки `agent/remove-lightweight-server-references` и `codex/ttl-backup-acceptance`.
|
||
|
||
**Как я буду проверять результат:** ни одна ссылка на action не содержит плавающего тега; `permissions` сужены именно в job'ах публикации, а не глобально; `SECURITY.md` содержит адрес и процедуру, а не заглушку; CHANGELOG соответствует реальным тегам, а не выдуман; CI зелёный; в `TEST_EVIDENCE.md` честно разделено локальное и CI; ревью получило текст задачи.
|
||
|
||
Если задача выполнена аккуратно и отчёт не приукрашен — переходим к содержательным задачам. Если в отчёте появятся непроверенные утверждения, это будет видно сразу и дёшево.
|
||
|
||
---
|
||
|
||
## 4. Дальнейшая очередь — Горизонт 0
|
||
|
||
Порядок обязательный. Горизонт закрывается целиком, Горизонт 1 не начинается до этого.
|
||
|
||
### Задача B — завершить B-3R · зависит от решения по п. 0.1
|
||
|
||
Контракт: `B3R_SPEC.md` (выдан ранее, принят исполнителем как authoritative).
|
||
|
||
По состоянию на 04.08 закрыто: R3 полностью (`lookup_node_ip` читает статус, `reserved` → exit 80, `active` с битым IP → 78) и R1 на уровне отдельной мутации (`VerifyExactFactualCountAsync` считает записи через `link-list`, условие `if (persisted)` вокруг верификации снято).
|
||
|
||
Осталось:
|
||
|
||
- пакетная финальная верификация: сейчас `1 + k` вызовов `link-list`, требуется ровно два — стартовый и один финальный на все затронутые ключи;
|
||
- класс `Deferred` и инвариант `Converged + Failed + Deferred == Examined`; `PendingActivation` — это `Deferred`, а не `Failed`, и он **не** блокирует потребление marker, иначе один зарезервированный Node вернёт бесконечный цикл проходов;
|
||
- R4: именованный DTO в source-generated контексте вместо анонимного типа для orphan audit, с проверкой на опубликованном trimmed-артефакте;
|
||
- R5: Linux integration test на fact-first протокол, с обязательным прогоном **на Linux**;
|
||
- R6: убрать fail-open default у `ILinkPolicyApplier.ListRulesAsync`;
|
||
- R7: форматирование вложенных lock-scope;
|
||
- дополнительно: мусор в поле статуса `nodes.tsv` должен давать 78, а не 80 — повреждённое состояние не то же самое, что ожидаемое.
|
||
|
||
**Готово, когда:** PR создан, Linux CI зелёный впервые для этой ветки, merge выполнен.
|
||
|
||
### Задача C — жизненный цикл сертификатов
|
||
|
||
`CertificateAuthority.cs:55` выдаёт клиентские сертификаты на год, автопродления нет. Через год после развёртывания парк отваливается одновременно, и восстановление потребует ручной перерегистрации каждого Node.
|
||
|
||
- срок клиентского сертификата 30–90 дней, значение в конфигурации с валидацией;
|
||
- автопродление Agent за треть срока до истечения через существующий mTLS-канал: новый CSR, выдача, атомарная замена `agent.pfx`, откат при неудаче;
|
||
- продление Operator-сертификата Desktop с уведомлением пользователя;
|
||
- событие `certificate.expiring` в поток событий;
|
||
- документированная процедура ротации Control CA и повторной регистрации парка — сейчас её нет;
|
||
- тесты: продление до истечения, недоступный Hub в момент продления, отказ при отозванном сертификате.
|
||
|
||
Файлы `CertificateAuthority.cs`, `CertificateLifecycleService.cs`, `AgentClient.cs` веткой B-3R не затронуты — задачу можно вести параллельно задаче B.
|
||
|
||
### Задача D — роль Monitor в bootstrap
|
||
|
||
SSH-мониторинг — функция, ради которой существует Desktop, — не имеет серверной части. В bootstrap нет `install-monitor`, forced-command скрипт не поставляется, а парсер `SshMonitorService.QueryAsync` ожидает ключи `PROTOCOL`, `CPU_COUNT`, `MEM_AVAILABLE_KB`, `DISK_INODES_TOTAL`, которых в репозитории никто не производит.
|
||
|
||
- действия `install-monitor PUBLIC_KEY` и `uninstall-monitor`;
|
||
- пользователь `ochenstarik-monitor`: nologin, без пароля, без sudo;
|
||
- `/usr/local/libexec/ochenstarik-smm-metrics`, root-owned `0755`, выдающий ровно снимок из `docs/installer-contract.md` §7 и read-only `mesh status`;
|
||
- `authorized_keys` с `command="…",restrict,no-pty,no-agent-forwarding,no-port-forwarding,no-X11-forwarding`;
|
||
- идемпотентность повторного запуска и корректное удаление;
|
||
- контрактный тест формата снимка, общий для скрипта и парсера Desktop.
|
||
|
||
Bootstrap-скрипт затронут веткой B-3R — делать после merge задачи B.
|
||
|
||
### Задача E — подписанная поставка
|
||
|
||
Bootstrap сверяет `ARCHIVE.sha256`, лежащий рядом с архивом в том же релизе, а manifest не подписан и содержит только хэш самого bootstrap. Кто может опубликовать релиз — ставит произвольный root-код на весь парк через `update-agent`.
|
||
|
||
- manifest v2: хэши всех артефактов, версии Control/Agent/helper/Desktop, минимальные совместимые версии, `helper_protocol`;
|
||
- подпись `cosign sign-blob` keyless либо `minisign`; публичный ключ или issuer **вшит константой** в bootstrap и Desktop, а не скачивается вместе с релизом;
|
||
- действие `verify-manifest`; `verify_archive` сверяет хэш с manifest, а не с соседним файлом; старый путь только под `SMM_ALLOW_UNSIGNED=1`;
|
||
- отказ `update-control` и `update-agent` при несовместимых версиях — требование `docs/installer-contract.md` §6, не выполненное до сих пор;
|
||
- `PROGRAM_VERSION` из тега вместо `0.2.0-dev`;
|
||
- отсутствие инструмента проверки подписи — отказ, не предупреждение;
|
||
- негативные тесты в CI: изменённый байт архива, подменённый хэш в manifest, manifest без подписи.
|
||
|
||
### Задача F — инженерная оснастка
|
||
|
||
- `Directory.Build.props`: `Nullable`, `TreatWarningsAsErrors`, `EnableNETAnalyzers`, `AnalysisLevel latest-recommended`, детерминированная сборка;
|
||
- `Directory.Packages.props` и `RestorePackagesWithLockFile` с коммитом lock-файлов — сейчас сборки невоспроизводимы, что противоречит требованию закреплённой поставки;
|
||
- разделить тесты на `Core.Tests`, `Control.Tests`, `Agent.Tests`, `Helper.Tests`: сегодня `LinuxMetricsTests`, `MetricBufferTests` и `ProvisioningHelperTests` лежат в проекте Control;
|
||
- тесты round-trip сериализации контрактов: при `PublishTrimmed=true` молчаливая потеря поля не ловится ничем.
|
||
|
||
`TreatWarningsAsErrors` включать после merge задачи B, иначе сломает незавершённую ветку.
|
||
|
||
### Гейт Горизонта 0
|
||
|
||
Чистый сервер ставится из подписанного релиза одной командой, регистрируется одноразовым кодом, попадает под мониторинг без ручной правки `authorized_keys`, и после перезагрузки Hub и Node фактическое состояние Links совпадает с желаемым — измеренное связностью на реальной топологии.
|
||
|
||
Последнее не может закрыть ни один исполнитель. Это единственное, что зависит только от выдачи доступов, и оно дешевле любой задачи в этом плане.
|
||
|
||
---
|
||
|
||
## 5. Горизонт 1 и далее
|
||
|
||
Не выдавать до закрытия гейта Горизонта 0. Состав и порядок — по `docs/product-horizons.md` и разделу «Горизонт 1» документа `smm-codex-plan-2026-08-04.md`: каркас `IProvisioningModule` первым, затем модули по одному, политики подтверждения, миграция SSH, alert engine, Telegram только на чтение, наблюдение Docker и systemd, Backup Manager.
|
||
|
||
Ключевое правило переноса: **не добавлять второй provisioning-модуль копированием timezone-исполнителя.** Сначала каркас, иначе шесть копий логики backup → mutate → verify → rollback.
|
||
|
||
---
|
||
|
||
## 6. Как оценивать исполнителя
|
||
|
||
По задаче A смотреть на четыре вещи, в порядке важности:
|
||
|
||
1. **Честность отчёта.** Разделены ли локальные проверки, CI и то, что не запускалось. Появление в отчёте непроверенных утверждений — худший признак из возможных, потому что обесценивает все последующие отчёты.
|
||
2. **Соответствие постановке, а не её пересказ.** Выполнены ли все восемь пунктов, а не пять удобных.
|
||
3. **Отсутствие расползания.** Не тронуты ли файлы вне области задачи.
|
||
4. **Качество ревью.** Получил ли независимый ревьюер полный текст задачи и нашёл ли что-нибудь, кроме опечаток.
|
||
|
||
Если задача A пройдена — давать задачу C (сертификаты): она содержательная, файлы свободны, и результат тоже проверяется объективно. Задачу B (завершение B-3R) давать только после того, как решён вопрос 0.1 и подтверждено, что исполнитель понимает контракт: это самая тонкая часть системы, там уже дважды находили блокирующие дефекты.
|