13 KiB
WALKTHROUGH — OpenSpec Lab (капитанский журнал)
Цель: воспроизводимость spec-driven подхода для инфраструктуры. Хронология по датам.
2026-09-08
Grafana: дашборд vinograd-wan — ошибка "Datasource grafana was not found" (change fix-vinograd-dashboard-datasource)
openspec new change fix-vinograd-dashboard-datasource → 4 артефакта
(proposal с Why/What Changes) → apply → validate → archive.
- Симптом: при открытии
/d/vinograd-wan/vinograd-wanокно "Failed to retrieve datasource / Datasource grafana was not found". - Причина: в JSON дашборда секция
annotations.listссылалась на встроенный датасорс{type: grafana, uid: __grafana__}(аннотации/алерты). В БД Grafana 11 OSS его нет (только Prometheus + Loki) → 404 при открытии. - Фикс:
jq '.annotations.list = []' ...— как в рабочем garage-cluster.json. Провайдер дашбордов перечитал за ≤30с (version 2), без рестарта. - Урок архивации: change с MODIFIED-заголовком, которого нет в существующей спеке, архив отклонит — нужен ADDED (новое требование), либо точное совпадение заголовка. OpenSpec архивация строгая.
- monitoring запушен (5ea6fd2, master); openspec-lab — следом.
Grafana read-only пользователь (it@vinogorod.ru, Viewer) — change grafana-readonly-user
openspec new change grafana-readonly-user → 4 артефакта → validate → archive
(delta → openspec/specs/grafana-access-control/spec.md).
- Задача: добавить read-only пользователя для IT Винограда.
- Главный вывод: Grafana 11 OSS НЕ поддерживает ни файловое provisioning
пользователей (
grafana/provisioning/access-control/users.ymlмолча игнорируется — это EE/Cloudsecurity.provisioning), ни API-создание (POST /api/users→ 404). Создание — только в UI (Administration → Users → New user, роль Viewer). - Приятный бонус:
POST /api/login(JSON) даёт 401 даже при верном пароле, а Basic auth работает (curl -u estorozhenko:пароль /api/user→ 200). - Проверено end-to-end: вход it@vinogorod.ru → 200, роль Viewer,
/api/users→ 403 (read-only), неверный пароль → 401. Пользователь вошёл сам. - git: мониторинг-репо запушен (da1746c, ветка master — не main!).
Vinograd WAN — ICMP-мониторинг канала «Винный город» (РТК), change в /opt/monitoring
openspec new change vinograd-rostelecom-channel-monitoring → 4 артефакта → validate OK → archive (delta → openspec/specs/vinograd-wan-monitoring/spec.md)
- Источник адресов:
/mnt/vinogorod/ИТ/Реестр внешних каналов связи.ods(это ODS, не XLSX), закладкаВинный_город: шлюз 83.239.50.145, оборудование 83.239.50.146. - Реализация: blackbox-exporter модуль
icmp+ prometheus jobvinograd_wan(scrape_interval 30s, metrics_path /probe, params module: [icmp], relabel instance → vinograd-gw/cpe) + алерт VinogradRostelecomDown + дашборд vinograd-wan. - Проверено: ICMP-проба .146 → probe_success=1 (RTT ~13ms), .145 → probe_success=0 (шлюз ДО СИХ ПОР DOWN — совпадает с UptimeKuma 08:07 MSK).
promtool check config→ 7 rules. Дашборд в grafana.db (uid vinograd-wan). - Питфол: YAML static_configs — labels относится к списку, не к элементу; regex IP экранировать точки.
- Питфол: retention per-job НЕ существует в Prometheus — глобальный 30d перекрывает «неделю» с запасом.
Нюансы OpenSpec при работе
openspec instructions <id>может ВИСЕТЬ (сетевая проверка/телеметрия) — проще писать артефакты руками по образцу archive/.OPENSPEC_TELEMETRY=0перед CLi-командами — не шумит и не висит.openspec validate/archiveзапускать ИЗ КОРНЯ openspec-lab, а не из /opt/monitoring.
2026-09-06
Установка OpenSpec
npm install -g @fission-ai/openspec # CLI 1.12.0, node 22
openspec init --tools hermes --force --no-animation # в /opt/hermes/openspec-lab
# ВАЖНО: опции --no-init-git НЕТ (упало). Использовать --force --no-animation.
Сгенерировано 6 Hermes-скиллов: .hermes/skills/openspec-{propose,apply-change,explore,update-change,sync-specs,archive-change}/SKILL.md
Подключение к Hermes: hermes config set skills.external_dirs '["/opt/hermes/openspec-lab/.hermes/skills"]'
config.yaml (инфраструктурный контекст)
- Пути: /opt//, systemd-юниты, docker compose, gitverse как источник истины
- rules: rollback в design.md, MUST/SHOULD, GIVEN/WHEN/THEN с проверочными командами
- НЮАНС: CLI 1.12.0 предупреждает про rules для 'specs' (папка мн.ч.) — формат верный, баг CLI, не влияет.
Демо-цикл add-vpn-tunnel-proxy
openspec new change add-vpn-tunnel-proxy → 4 артефакта → validate OK → archive --yes (delta → openspec/specs/tunnel-proxy/spec.md, change → archive/2026-09-06-*). Работает.
Tavily-прокси (web_extract) — диагноз и фикс
- Проблема: web_extract не работал (extract_backend='' → пусто)
- Tavily из РФ: api.tavily.com → 403 (AWS ELB geo-block). Через
--socks5-hostname 127.0.0.1:1080(ssh-туннель telegram-tunnel до VPS01) → 200. - Решение: локальный HTTP→SOCKS5 форвардер /opt/hermes/.hermes/scripts/tavily_proxy.py (порт 8971), TAVILY_BASE_URL=http://127.0.0.1:8971 в .env. Прокси для Hermes НЕ выставлять глобально (сломало бы прямой трафик к deepseek/polza).
- systemd: /etc/systemd/system/tavily-proxy.service — After/Wants=telegram-tunnel.service, Restart=always, User=estorozhenko.
- Питфол: FakeReq в http.server не имеет method → curl HTTP 000; починил
method = self.command. - Питфол:
sedпо# TAVILY_API_KEY=не нашёл строку (её не было) — ключ добавил в конец .env. Бэкап .env.bak-20260906. - Проверка e2e: Hermes-провайдер (
_tavily_requestиз plugins/web/tavily/provider.py) → doc Example Domain с контентом.
Локальный экстрактор (change local-extractor, открыт)
Расширил tavily_proxy.py → tavily_extract_proxy.py:
_fetch_page(url, socks)— httpx, при socks → SOCKS5-transport_extract_local(url, socks)— trafilatura.extract → markdown; метаданные через extract_metadata- CLI:
--local(по умолчанию без --socks),--local-socks HOST:PORT,--socks HOST:PORT --upstream(forward) - Формат ответа = Tavily /extract:
{"results":[{url,title,raw_content,metadata}], "failed_results":[...], "failed_urls":[...]}— Hermes-провайдер обрабатывает как есть. - trafilatura 2.2.0 установлен в venv Hermes.
- Питфол:
trafilatura.fetch_url(url, timeout=...)— СИГНАТУРА ИЗМЕНИЛАСЬ, timeout не принимает (упало) → заменил на httpx-клиент с таймаутом. - systemd-юнит переведён на tavily_extract_proxy.py (forward-режим,
--socks 127.0.0.1:1080 --upstream https://api.tavily.com) — рабочий. - Тесты 2.2/2.3 (внешние URL) НЕ ПРОШЛИ: командная строка блокирует внешние запросы без подтверждения пользователя; отложено.
Git / gitverse
git init -b main && git add ... (НЕ git add -A — зависает; добавлять файлы поимённо)
git -c user.name=... -c user.email=... commit -m "..."
git remote add origin https://estorozhenko:<TOKEN>@gitverse.ru/estorozhenko/openspec-lab.git # УСТАРЕЛО: логин kpa39l, см. ниже
Токен: obsidian homelab/gitverse.ru.md (dc52c489...) и homelab/gitea.nixg.ru/«Токен для gitverse.ru.md» (a8c8f69e..., для зеркала gitea).
- Питфол:
git add -Aзависал (блок сессии) — добавлять файлы явно поимённо. - Push НЕ делал (создание репо на gitverse — внешнее действие, ждёт пользователя).
Сетевые тесты local-режима (2.2 / 2.3) — ЗЕЛЁНЫЕ
Запуск: /opt/hermes/.hermes/hermes-agent/venv/bin/python /opt/hermes/.hermes/scripts/tavily_extract_proxy.py --port 8972 --local.
- 2.2:
curl POST 127.0.0.1:8972/extract {"urls":["https://example.com"]}→ HTTP 200, 0.98s,raw_content= «This domain is for use in documentation examples...». GitHub:https://github.com/→ HTTP 200, 0.84s, полный текст главной страницы. Хвост: процессы убиты, порт 8972 свободен. - 2.3:
--local-socks 127.0.0.1:1080(туннель telegram-tunnel active) наhttps://tavily.com/(geo-блок из РФ) → HTTP 200, 3.7s, контент Tavily извлечён (302 → www.tavily.com → 200). Туннель подтверждён. - OpenSpec:
openspec validate local-extractor→ valid;openspec archive local-extractor --yes→ новая specopenspec/specs/web-extract-local/spec.md(+3), change → archive/2026-09-06-local-extractor.
GitVerse API (создание репозитория)
- UI/SPA:
https://gitverse.ru/api/v1/...отдаёт HTML (Next.js) — НЕ JSON. Старые ссылки /api/v1 из WALKTHROUGH не работают. - Правильный API:
https://api.gitverse.ru(Gitea-совместимый: /user/repos, /repos/{owner}/{repo}/...) - Создание:
POST https://api.gitverse.ru/user/reposсAuthorization: Bearer <токен>+Accept: application/vnd.gitverse.object+json;version=1→ HTTP 201, id 327605. - ВАЖНО: фактический логин аккаунта = kpa39l (не estorozhenko!). Remote пришлось поправить:
git remote set-url origin https://kpa39l:<TOKEN>@gitverse.ru/kpa39l/openspec-lab.git. Репо: https://gitverse.ru/kpa39l/openspec-lab. git push -u origin main→* [new branch] main -> main, tracking установлен.
Решение по systemd (задача 5) — forward оставлен
- Вопрос задан пользователю через clarify: переключать ли tavily-proxy.service на --local.
- Решение пользователя: оставить forward-режим как есть (облачный Tavily через туннель). Юнит не менялся: ExecStart=... --port 8971 --socks 127.0.0.1:1080 --upstream https://api.tavily.com.
Подводные камни (сводно)
- openspec init: нет --no-init-git.
- trafilatura.fetch_url не принимает timeout (2.2.0).
- FakeReq.http.server без method → 000.
- git add -A зависает в этой сессии; только поимённо.
- Внешние curl (github, tavily через прокси) блокируются без ответа пользователя — для сетевых тестов нужно его присутствие.
- Прокси Hermes глобально не трогать (deepseek/polza прямые).
- Tavily требует туннель; .env — бэкап перед правкой.
- gitverse.ru/api/v1/* → HTML (SPA). Только api.gitverse.ru отдаёт JSON; логин аккаунта kpa39l.
Как продолжить (следующая сессия)
- Все 5 задач закрыты (2.2/2.3 зелёные, archive сделан, репо запушено, systemd — решение принято, forward остался).
- Лаба в стабильном состоянии. Дальше — по желанию: автоматизация сборки (миграция Gitea-зеркала на gitverse, если потребуется) либо новые changes по инфраструктуре.