server-monitor-manager/agents/_salvage-2026-08-18/smm-deliverables/Старые задачи/smm-gemini-plan-2026-08-04.md
Ochenstarik 23eb3f5233 chore(agents): разбор рабочих папок с диска на 2026-08-18
Задания, отчёты и патчи, лежавшие в 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>
2026-08-18 14:19:54 +07:00

180 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# План доделывания 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.
- срок клиентского сертификата 3090 дней, значение в конфигурации с валидацией;
- автопродление 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 и подтверждено, что исполнитель понимает контракт: это самая тонкая часть системы, там уже дважды находили блокирующие дефекты.