# Fuel Watch

> Проверяет текущее наличие АИ-95 независимо от брендового или премиального названия, свежесть сигналов и очереди на АЗС в настроенной зоне Волгограда через agent-browser, хранит 7-дневную историю и прогнозирует ближайшие появления. Используй при запросах «где есть 95-й», «проверь бензин/АЗС/очереди», «когда появится или появился бензин» и «следи за наличием». Не используй для маршрутизации, отправки пользовательских отчётов на сайты или обхода CAPTCHA.

- Skill: `shkarupa-alex/fuel-watch` (Agent Skill, multi-file: 79 files)
- Install (CLI): `npx skillmds@latest add shkarupa-alex/fuel-watch`
- Raw SKILL.md: https://api.skillmd.com/api/skills/shkarupa-alex/fuel-watch/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: shkarupa-alex (https://skillmd.com/u/shkarupa-alex)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/shkarupa-alex/fuel-watch

---


# Fuel Watch

Показывай только АЗС со свежим положительным или вероятно положительным свидетельством в основном списке. Все варианты одного октанового числа считай одной пользовательской маркой: базовый, премиальный и брендовый 95-й выводятся только как `АИ-95`, без отдельных строк `95+`, «Экто», G-Drive и подобных. Исходную метку варианта сохраняй внутри только для provenance и консервативной проверки отрицательных данных. Отказ источника, пустая выборка и отсутствие бензина — разные результаты. Всегда называй здоровье каждого источника, свежесть и уверенность, а также предупреждай, что страничные/краудсорсинговые данные могут запаздывать. Каждый отчёт также содержит до трёх прогнозов ближайшего появления по накопленной истории либо явное сообщение, что статистики пока недостаточно.

## Неприкосновенный формат отчёта

Вывод `report.mjs` — единственный канонический пользовательский отчёт. Публикуй его содержимое как обычный Markdown без каких-либо изменений: не пересказывай своими словами, не сокращай, не переставляй строки, не объединяй с собственным списком и не удаляй Markdown-разметку. В частности, каждая сгенерированная ссылка на Яндекс Карты должна дойти до пользователя неизменной.

Не заключай отчёт в code fence или blockquote: ссылки должны остаться кликабельными. Допустима только отдельная короткая служебная фраза до или после полного отчёта, если нужно объяснить ошибку запуска; она не заменяет и не модифицирует сам отчёт. Если отчёт получен как JSON, декодируй поле `markdown` и опубликуй именно его значение. Перед отправкой проверь, что ни одна строка или ссылка из результата `report.mjs` не потеряна.

## Подготовка

Определи корень skill по расположению этого `SKILL.md` и используй его абсолютный путь как `FUEL_WATCH_ROOT`. Каталог skill — заменяемый read-only артефакт: никогда не создавай и не меняй в нём config, state, историю, `node_modules` или другие runtime-файлы. Пользовательские настройки и накопленные данные автоматически живут в `~/.fuel-watch/`: `config/`, `history/`, `state/` и `monitors/`. Переменная `FUEL_WATCH_HOME` может перенести весь этот каталог. Не меняй пользовательскую конфигурацию без явного запроса.

Сначала проверь внешний runtime командами `command -v agent-browser` и `agent-browser --version` в том же окружении, где будет выполняться collection. Если команда доступна, используй установленную версию без переустановки. Никогда самостоятельно не выполняй `npm install -g agent-browser`, `npm update -g agent-browser`, `npx agent-browser` или `agent-browser install`. Если команда отсутствует или не запускается, ничего глобально не устанавливай: верни `BROWSER_UNAVAILABLE`, покажи результат проверки и запроси у пользователя отдельное разрешение на изменение окружения.

Runtime поставляется заранее собранным в `dist/` вместе со всеми Node.js-зависимостями. На целевой машине никогда не выполняй `npm install`, `npm ci`, `npm update` или `npx` для Fuel Watch. Отсутствие `node_modules` в установленном skill штатно. Если `dist/scripts/collect.mjs` отсутствует, верни ошибку повреждённого/неполного skill-пакета; не пытайся собирать его на целевой машине.

В штатной collection сайты открываются только через `agent-browser`, которым управляют скрипты. Не добавляй `curl`, `fetch`, прямой HTTP-клиент, CAPTCHA solver, dashboard, HAR, video, trace, restore/profile или фоновый браузер.

Штатная collection всегда использует `HEADED` Chromium без stealth-флагов и подмены fingerprint. Если доступен пользовательский display, BrowserRunner сохраняет `DISPLAY`/`WAYLAND_DISPLAY`/`XAUTHORITY`; иначе `agent-browser` может использовать собственный Xvfb, но браузер всё равно остаётся non-headless. Не переноси результат `HEADLESS`-диагностики на обычный браузер или доступность сайта вообще. Каждый источник получает отдельную короткоживущую session, источники обрабатываются строго последовательно, поэтому одновременно активно не более одного окна.

BrowserRunner сначала обязательно устанавливает `--allowed-domains`. Только если agent-browser 0.35.1 возвращает известную CDP-ошибку установки network controls, он один раз пересоздаёт owned session без controls и сохраняет fail-closed проверки точного final host и последующего page drift. Такой результат всегда получает предупреждение `BROWSER_NETWORK_CONTROLS_DEGRADED`; не скрывай его и не называй fallback эквивалентом полноценной сетевой изоляции.

Штатный workflow не управляет вкладками и не собирает визуальные артефакты: он вызывает только скрипты ниже. Если пользователь явно просит показать браузер, исследовать DOM/network/console или отладить источник, сначала прочитай [references/browser-debugging.md](references/browser-debugging.md) и выполняй диагностику отдельно от collection-run.

## Разовая проверка

1. Создай временный каталог через `mktemp -d`.
2. Выполни:

   ```bash
   node "$FUEL_WATCH_ROOT/dist/scripts/collect.mjs" --output "$RUN_DIR/snapshot.json"
   node "$FUEL_WATCH_ROOT/dist/scripts/report.mjs" --snapshot "$RUN_DIR/snapshot.json"
   ```

3. Опубликуй stdout `report.mjs` целиком и неизменным обычным Markdown даже при коде 2: он объясняет деградацию и не означает «бензина нет». Не создавай вместо него собственную сводку.
4. При коде 75 предупреди о `CLEANUP_FAILED`, затем один раз проверь очистку только записанного owned namespace. Не запускай глобальный `agent-browser close --all` без namespace.
5. Удали временный каталог. Компактная история статусов и часовых rolling-count сигналов по октановым маркам 92/95/98/100 сохраняется отдельно в пользовательском state-каталоге, автоматически обрезается до последних 7 дней и используется следующими разовыми проверками и monitoring tick’ами. Перед сравнением соседних tick все варианты одного октанового числа агрегируются внутри `station + source + tick`: rolling-count суммируется; статус имеет приоритет `IN_STOCK → LIMITED → OUT_OF_STOCK → UNKNOWN`, поэтому нечитаемый вариант игнорируется при наличии хотя бы одного читаемого. Переход октана засчитывается только при свидетеле — один и тот же вариант присутствует в обоих tick и действительно изменился `OUT_OF_STOCK → IN_STOCK/LIMITED` или `count 0 → count > 0`; появление/исчезновение строки само по себе событием не является. Дизель, газ, неизвестные продукты и прочие октановые числа исключаются. Путь можно явно задать через `--history`; переменная `FUEL_WATCH_HISTORY_PATH` меняет default path.

## Мониторинг в текущей задаче

Не создавай automation, daemon или новую задачу. Мониторинг выполняет активный агент в этом диалоге до команды пользователя остановиться или внешнего прерывания.

1. Инициализируй монитор и сохрани напечатанные `monitorId` и абсолютный `stateDir` в контексте задачи:

   ```bash
   node "$FUEL_WATCH_ROOT/dist/scripts/monitor.mjs" init
   ```

2. Перед первым и каждым следующим tick проверь `monitor.mjs due --state-dir "$STATE_DIR"`. Когда tick наступил:
   - запусти `collect.mjs --previous "$STATE_DIR/previous.json"` только если такой snapshot подготовлен агентом из предыдущего committed state;
   - создай отчёт `report.mjs --snapshot "$SNAPSHOT" --state-dir "$STATE_DIR" --json`;
   - вызови `monitor.mjs prepare --state-dir "$STATE_DIR" --report-id "$REPORT_ID" --snapshot "$SNAPSHOT"`;
   - опубликуй значение `markdown` из JSON целиком, неизменным и не в code fence; не пересказывай его и не удаляй ссылки;
   - только после успешной публикации вызови `monitor.mjs commit --state-dir "$STATE_DIR" --report-id "$REPORT_ID"`.
3. Между tick’ами браузер уже закрыт. Жди foreground-командой `sleep 45` или короче. После каждого фрагмента проверяй новый пользовательский ввод, `monitor.mjs due` и `STOP` через его поле `stopped`; не запускай единый 15-минутный sleep.
4. После четырёх пустых tick’ов используй `report.mjs --compact`, но продолжай cadence.
5. При возобновлении задачи сначала вызови `monitor.mjs recover --state-dir "$STATE_DIR"`. Если есть pending report, повторно отрендери его с `--recovered`, сохрани тот же report ID и затем commit.
6. При остановке вызови `node "$FUEL_WATCH_ROOT/dist/scripts/monitor.mjs" stop --state-dir "$STATE_DIR"`, выполни одну namespace-scoped очистку последнего browser-run и заверши `node "$FUEL_WATCH_ROOT/dist/scripts/monitor.mjs" cleanup --state-dir "$STATE_DIR"`. Подтверди удаление monitor-state; общие настройки, последний snapshot и 7-дневная история сохраняются.

Никогда не держи браузер во время ожидания. Любая коллекция чаще настроенного `monitoring.intervalMinutes` запрещена в monitoring mode.

## Изменение зоны

Для проверки anchor-координат выполни read-only команду:

```bash
node "$FUEL_WATCH_ROOT/dist/scripts/resolve-area.mjs"
```

Покажи найденные координаты и drift пользователю. Перезапись разрешена только после явного подтверждения: повтори с `--write --confirm`. Три неуникальные или коллинеарные точки, неверные координаты и неразрешённый anchor приводят к fail-closed.

## Интерпретация

- `низкая`, `средняя` и `высокая` в отчёте всегда означают уверенность Fuel Watch в собственной оценке, а не заявленную источником вероятность. Она выводится из свежести и специфичности свидетельства, provenance-групп и grade-specific активности. Не приписывай её отдельному сервису. Непроверенное source confidence/rating можно сохранить только как provenance; не подменяй им итоговую уверенность без формализованной семантики и калибровки.
- `последний подтверждающий сигнал` — возраст самого свежего observation, реально участвующего в текущем положительном вердикте. `только что` означает возраст меньше одной минуты, а не обязательно изменение статуса или прибытие бензовоза. Источники текущей оценки и источники исторического перехода выводятся отдельными строками; не объединяй их в одну подпись.
- `ЕСТЬ`: свежий прямой сигнал любого варианта АИ-95. `СКОРЕЕ ЕСТЬ`: семейный/косвенный положительный сигнал или подтверждённое grade-specific возобновление активности. В пользовательском отчёте и истории варианты одного октанового числа не разделяй.
- Не называй generic signal транзакцией. `TRANSACTIONS_RESUMED` — только grade-specific разрыв не менее 60 минут и минимум два новых события за 20 минут.
- Не трактуй время обновления как время поставки. «Появился между…» допустимо только для подтверждённого перехода из `NOT_AVAILABLE`; иначе говори «впервые увидели» или «время появления неизвестно».
- Прогноз — не факт поставки. Сильнейший допустимый обучающий сигнал — типичное местное время перехода настоящего grade-specific rolling-count `0 → ≥2` по бензиновым маркам 92/95/98/100 после часового тихого окна. Не размножай агрегатный счётчик АЗС по маркам: живой Yandex сейчас отдаёт именно агрегатный `signalsCountPerHour`, поэтому он исключён из прогноза как потенциально содержащий дизельные сигналы. На текущих данных основной доступный признак общего подвоза — одновременный переход нескольких бензиновых статусов из `OUT_OF_STOCK` в `IN_STOCK/LIMITED`; одиночный переход слабее. Дизель, газ и неизвестные продукты не учитываются. Подтверждённые переходы union АИ-95 служат последним fallback. Сначала используй историю этой АЗС, затем бренда, затем зоны; всегда показывай интервал, тип сигнала, число эпизодов и уверенность. Не придумывай время в холодном старте и не скрывай нехватку данных.
- Разовые проверки тоже накапливают эту историю. Мониторинг не обязателен, но частые tick’и дают существенно лучшую временную разрешающую способность; редкие ручные запуски могут полностью пропустить короткий переход rolling-count.
- Presence-only очередь не сравнивается как короткая. Неизвестная очередь сортируется после известных.
- Yandex, gdebenz и benzonavt относятся к одной provenance group по умолчанию и сами по себе не дают независимого многоисточникового повышения уверенности.
- У Yandex station-level `lastSignalTimestamp` и `signalsCountPerHour` не относятся к конкретной марке и не должны давать ей свежесть или повышать уверенность. Grade-наблюдение использует только timestamp/count из самой строки марки; агрегатное время допустимо для очереди и контекста АЗС. Если живой payload не содержит grade-time, источник честно возвращает `PARTIAL/NO_GRADE_FRESHNESS_METADATA`, а не `COMPLETENESS_INVARIANT` и не свежий вердикт.
- У Benzonavt `fuels_now` трактуется как исчерпывающий текущий список: непустой список без 95 — family-negative. Любой `st.conflict` до формализации его семантики переводит топливное наблюдение в `UNCERTAIN` и запрещает как рекомендацию, так и family-wide отрицание. `queue.size=20_50/gt50` отображай как `LONG/VERY_LONG`, используя собственное `queue.at`.
- Каталожный список топлива 2GIS не доказывает текущее наличие; учитывай только явный current-status с `last_report_at`. CAPTCHA означает `CHALLENGE`; не пытайся её решать.
- Семидневная история связывает физическую АЗС по сохранённым member IDs, а не только по изменчивому merged `stationKey`, и сериализует параллельные read–modify–write через bounded lock. `HISTORY_LOCK_TIMEOUT` означает недоступный прогноз, но не отменяет текущий отчёт.

## Пример

Запрос: «Где сейчас есть 95-й, и где очередь меньше?»

Ожидаемый результат: один отчёт с timestamp и зоной, здоровьем Yandex/gdebenz/2GIS/Benzonavt, изменениями при наличии предыдущего tick, ранжированными только положительными АЗС, единственной агрегированной строкой `АИ-95` со свежестью и уверенностью для каждой станции, отдельными счётчиками отрицательных/неизвестных данных, прогнозом до трёх ближайших появлений и обязательным предупреждением.

## Ошибки

- `CHALLENGE`, `HTTP_ERROR`, `TIMEOUT`, `RESOURCE_BLOCKED`, `SCHEMA_CHANGED`: назови источник и продолжи с остальными.
- Все источники деградировали: сообщи «нет доступных свежих данных», не «бензина нет».
- `CLEANUP_FAILED` / код 75: данные можно показать, но предупреждение обязательно; выполни одну повторную namespace-scoped проверку очистки до ожидания или новой коллекции.
- Невалидная конфигурация / код 2 до старта браузера: покажи точный JSON path из ошибки и ничего не собирай.
- `HISTORY_UNAVAILABLE`: текущие данные можно показать, но прогноз недоступен; назови ошибку записи/чтения и не подменяй её выдуманным временем.
- Временный state не удалился: сообщи абсолютный каталог и не утверждай, что monitoring полностью остановлен.

## Проверка разработки

После изменения skill выполни `npm test`. Live smoke tests отделены и запускаются только по явному запросу: `npm run test:live`. Они никогда не входят в 15-минутный monitoring loop.

