Files
openspec-lab/WALKTHROUGH.md
T

135 lines
13 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.
# 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/Cloud `security.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 job `vinograd_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
```bash
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/<service>/, 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
```bash
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` → новая spec `openspec/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.
## Подводные камни (сводно)
1. openspec init: нет --no-init-git.
2. trafilatura.fetch_url не принимает timeout (2.2.0).
3. FakeReq.http.server без method → 000.
4. git add -A зависает в этой сессии; только поимённо.
5. Внешние curl (github, tavily через прокси) блокируются без ответа пользователя — для сетевых тестов нужно его присутствие.
6. Прокси Hermes глобально не трогать (deepseek/polza прямые).
7. Tavily требует туннель; .env — бэкап перед правкой.
8. gitverse.ru/api/v1/* → HTML (SPA). Только api.gitverse.ru отдаёт JSON; логин аккаунта kpa39l.
## Как продолжить (следующая сессия)
1. Все 5 задач закрыты (2.2/2.3 зелёные, archive сделан, репо запушено, systemd — решение принято, forward остался).
2. Лаба в стабильном состоянии. Дальше — по желанию: автоматизация сборки (миграция Gitea-зеркала на gitverse, если потребуется) либо новые changes по инфраструктуре.