# AGENT.MD — Правила проекта VESTI ## Правило №1: ВСЕ изменения — через OpenSpec - **Перед каждым планируемым изменением — обязательно grill-with-docs** (интервью по дизайн-дереву: глоссарий в CONTEXT.md + ADR при необходимости). Только после достижения общего понимания с пользователем — создавать change. - **Любое изменение проекта (багфикс, фича, рефакторинг, конфиг, деплой, docs) оформляется как отдельный change** в `/opt/vesti/openspec/changes//`. - **НЕ начинать правки кода/файлов, пока change не создан и не провалидирован (`openspec validate` чисто).** Это обязательное требование (у VESTI нет бэкапа — OpenSpec фиксирует состояние и откат). - **Сразу после правок — обновить STATUS.md/TODO.md и сделать бэкап** (см. «Бэкап» ниже). - Формат change (как в образцах `tg-crawler-publisher-prototype`, `publisher-service`): - `.openspec.yaml` (schema: spec-driven, created: дата) - `proposal.md` — зачем (проблема пользователя) - `design.md` — как (дизайн, точные правки) - `tasks.md` — чеклист задач; отмечать `[x]` при выполнении - CLI `openspec change new` НЕ существует → change создаётся вручную (mkdir + файлы по образцу). - Проверка: `openspec validate ` — должно быть чисто. - После подтверждения пользователем change архивируется. - Не начинать правки кода, пока change не создан и не провалидирован. ## Стек и архитектура - Новостной агрегатор: TG-краулер → классификатор (Ollama qwen3:8b-nothink) → веб-модерация → публикация в Telegram. - Веб: :8400 — страницы /candidates (модерация), /crawlers (статус краулеров, запуск воркера), /sources (управление источниками: add/edit/toggle-pause/delete; источник истины — sources.yaml). - `vesti-web` (FastAPI+Jinja2, :8400, systemd-юнит) --HTTP POST--> `publisher-service` (FastAPI, Docker-контейнер, :8410) --Bot API (SOCKS5 127.0.0.1:1080)--> Telegram @dedinit_vesti. - БД: SQLite `/opt/vesti/db/vesti.db` — открывать с WAL + busy_timeout (иначе `database is locked` при фоновом классификаторе). - Фронтенд: **без внешних CDN** (никаких unpkg/jsdelivr). Bootstrap локален: `web/static/bootstrap.min.css`, htmx запрещён. approve/reject — обычные POST-формы. - Markdown в шаблонах — фильтр `| markdown` (XSS-safe: escape → nl2br → sane_lists). Сырой вывод текста запрещён. - Даты в UI — фильтр `| dt` → `ЧЧ:ММ ДД.ММ.ГГГГ` (не ISO `[:16]`). - Пути бандлов: в БД `published.bundle_path` хранится **относительный** путь от `bundles/` (напр. `linux/2026-09/slug.md`), ссылка в UI `/bundle/`. Абсолютные пути запрещены (баг двойного слэша — исправлен). ## Секреты и .env - `.env` в корне (chmod 600). Переменные: `ADMIN_USER`, `ADMIN_PASSWORD` (веб-логин), `VESTI_BOT_TOKEN`, `VESTI_BOT_CHANNELS`, `TG_PROXY`, `PUBLISHER_URL`, api_id/api_hash. - Значения секретов НЕ показывать, НЕ сохранять, НЕ коммитить (пишется только имя переменной). - `.env.example` — всегда актуализировать при добавлении переменной. ## Правила работы - **Не удалять пользовательские файлы.** Перезапись/перемещение — только после явного подтверждения пользователя. - Дедуп постов: sha256(text) + url. Физически ничего не удаляем — только смена `status`. - Общение с пользователем — на русском, кратко (статусы), без «всё ок». - Все ресурсы проекта — в `/opt/vesti` (ничего в домашней директории). - Документация проекта: STATUS.md (живой), PRD.md, TODO.md, WALKTHROUGH.md — поддерживать актуальными; при закрытии сессии обновлять. - Ссылки и доступы к ресурсам (URL/SSH/пути) фиксировать в файлах проекта. ## Запуск / проверка ```bash cd /opt/vesti sudo systemctl restart vesti-web # веб :8400 (0.0.0.0, uvicorn web.app:app) docker compose -f services/publisher/docker-compose.yml up -d --build # publisher :8410 curl -s http://127.0.0.1:8410/healthz # ok, bot=dedinit_controller_bot, proxy, channels .venv/bin/python -m crawler.telegram_crawler --all # краулер (инкрементально) .venv/bin/python -m crawler.rss_crawler --all # RSS/Atom-краулер (условные GET, etag/modified; rss_state) .venv/bin/python -m crawler.rss_crawler --source lwn --dry-run # сухой прогон без записи в БД .venv/bin/python -m crawler.crawl_sources --all # фаза 1: сбор кандидатов в очередь (telegram+rss) .venv/bin/python -m crawler.worker --workers 8 --limit 200 # фаза 3: параллельная обработка очереди (ThreadPoolExecutor) MAX_CONCURRENT_LLM=2 .venv/bin/python -m crawler.worker --workers 8 # лимит одновременных LLM-запросов к Ollama CLASSIFY_TIMEOUT=20 .venv/bin/python -m classifier.classify --db db/vesti.db --limit 200 openspec validate # валидация change ``` - Внешний доступ: https://vesti.nixg.ru (Caddy на VPS02: /opt/caddy/Caddyfile, reverse_proxy 10.8.0.2:8400). ## Направления (directions) linux, tech, politics, games, electronics, llm. Свой канал @dedinit — источник `own: true`, посты is_own=1, fan-out по направлениям с атрибуцией «Дед в АйТи». ## Бэкап (автоматический, вручную НЕ запускать) - Скрипт: `/opt/vesti/backup.sh` (по образцу `/opt/icq/backup.sh`): архивирует проект → `/opt/vesti/backups/vesti_<дата>.tar.gz`, копирует на Яндекс.Диск `/mnt/yandex-disk/backup/vesti-backups/`. - Хранение: локально 7 дней, на ЯД 30 дней (ротация в скрипте). - Запуск: root cron `45 2 * * *` (как у icq). ЯД монтируется автоматически (fstab davfs + @reboot). - Бэкапы запускаются автоматически по крону — вручную НЕ запускать.