Compare commits

...

10 Commits

Author SHA1 Message Date
79adaf928d docs(AGENTS.md): канон-блок — краткая суть гейтов вместо обрубков (task:3017) 2026-09-18 22:12:27 +03:00
b51657bfe4 docs(AGENTS.md): канон-блок — краткая суть гейтов вместо обрубков (task:3017) 2026-09-18 21:58:28 +03:00
48fa29e9dc docs(AGENTS.md): канон-блок — краткая суть гейтов вместо обрубков (task:3017) 2026-09-18 21:25:56 +03:00
65a2518a5e docs(canon): канон-блок AGENTS.md из живого shared-среза (task:2882)
Блок перегенерирован писателем кэша, поставленным в mappa-setup 0.2.6
(--gen-canon-block: живой shared → кэш .mappa/share/ → блок из ТОГО ЖЕ среза).

- строка Entity → runbook снова несёт полный каталог типов из runbooks/index
  (issue, intent, requirements, plan, comment, tag, attachment, release,
  brainstorm, agent, repo, project, skill, entity, sched) — до этого в блоке
  оставалась усечённая карта;
- Canon version — версия КАНОНА (canon/*), не максимум по методологии/ранбукам.
2026-09-18 00:28:08 +03:00
84e28c5d1c docs: канон-блок AGENTS.md — Г2 v4 (адресация about/thread) + сняты junk mail-строки (task:2993) 2026-09-17 23:41:40 +03:00
607a475e28 feat(ops-browser): скил своего скрытого браузера (профиль+CDP+хендофф) в каталог; маппинг в Hermes-дерево; строки в README 2026-09-12 12:30:05 +03:00
252e22ec80 fix(browser-operator): PID аренды только настоящий (/proc/$$/winpid), driver.lock руками не трогать 2026-09-11 23:31:37 +03:00
ac0c41feb8 chore: .gitignore — .mappa/share/ (кэш bootstrap, генерируется) 2026-09-11 23:13:32 +03:00
314b15ea25 feat(browser-operator): скил-водитель для браузера оператора (канал по харнессу + аренда)
- skills/browser-operator/SKILL.md: канал по харнессу (Hermes browser_exec / pi тул browser /
  CC chrome-devtools), аренда «один водитель за раз», границы человек/агент, рецепты тяжёлых
  страниц (fetch внутри вкладки, паузы в Python, AX-дерево для кликов), таблица антипаттернов
- основание — RED-прогон без скила (2026-09-11): агент ушёл в curl + browser_cookie3 + ввод
  пароля мимо канала и аренды и не знал адрес кабинета ЧипДипа (/order/list, а не /cabinet)
- browser-cdp 0.1.1: разведены зоны (личные кабинеты оператора -> browser-operator)
- hermes/mapping.yaml: browser-operator (auto/software-development) — сейчас конвертер
  красный из-за 14 ранее незамапленных скилов, это отдельная находка
- README + README.ru: строка провенанса
2026-09-11 22:12:21 +03:00
c310ada38d chore(marker): .mappa/config.yaml — git_host (issue:30, task:2558) 2026-09-06 16:39:41 +03:00
12 changed files with 258 additions and 3 deletions

3
.gitignore vendored
View File

@@ -90,3 +90,6 @@ coverage/
# Missing here made `git status` see `?? .tasks/claims/` → poller skipped every
# claim with "working tree dirty". Mirrors .common/.gitignore.
.tasks/claims/
# mappa bootstrap cache (генерируется, не в репо)
.mappa/share/

View File

@@ -6,3 +6,4 @@ tenant: vitya
url: https://mappa.vds.kzntsv.site
git_provider: gitea
git: OpeItcLoc03/skills
git_host: git.kzntsv.site

View File

@@ -8,10 +8,27 @@ check across all projects
pull remote before work
session handoff: read on start, write on end
inbox monitor: raise on start
session sync: write to mappa
follow project discipline
follow tdd-criteria
delegate to interns when allowed
recommend, don't menu
we're on Windows
mappa sync: run on start
<!-- mappa:canon-block (auto; do not edit) -->
## Mappa canon
**Gates (canon/gates — shared):** краткая суть; полный текст — `wiki_get(slug='canon/gates', full=true)`
- Г1. Знание и артефакты → mappa, не файлы — durable-знание и артефакты проекта живут в mappa; файловые каналы (`.brainstorm/`, `.tasks/`, `.wiki/`) и «сохранить рядом с проектом» закрыты.
- Г2. Контракт каналов — письмо (inbox) — носитель вердиктов/указаний/находок, полным телом; комментарии на тасках — короткий след. Адресация `about`/`to`/`thread` (XOR); lifecycle `ack`/`resolve`/`cancel`.
- Г3. Поиск до угадывания — не угадывать slug/роут: сначала `search`/`wiki_search`; `wiki_get` без project = shared; общая память — `search(mode='recall')`.
- Г4. Адресация и слаги — рефы полными именами (`[[task:N]]`/`[[wiki:slug]]`); слаги kebab-case, латиница; номера `task:N` выдаёт сервер.
- Г5. .mappa-гейт — папка участвует в mappa-операциях только с маркером `.mappa`; нет маркера → сказать человеку, мутации — отказ.
- Г6. Секреты — в mappa не пишутся (422); только `secret:<path>`-рефы, значения мимо.
- Г7. Degraded-режим — mappa недоступна: читать кэш `.mappa/` (canon/methodology/runbooks), мутации → `.mappa/pending/`; нет кэша → стоп, не импровизировать.
- Г8. Перед работой с вики/каноном — первым действием прочитать канон-блок AGENTS.md проекта.
- Г9. Живое состояние до заявления — статус заявлять только по свежему чтению mappa, не по памяти/кэшу/ответу create.
**Entity → runbook (runbooks/index — shared):** task → [[runbooks/tasks]] · wiki → [[runbooks/wiki]] · inbox → [[runbooks/inbox]] · **thread** → [[runbooks/threads]] · session → [[runbooks/session]] · search → [[runbooks/search]] · issue → [[runbooks/issue]] · **intent** → [[runbooks/intent]] · requirements → [[runbooks/requirements]] · plan → [[runbooks/plan]] · comment → [[runbooks/comment]] · tag → [[runbooks/tag]] · attachment → [[runbooks/attachment]] · release → [[runbooks/release]] · brainstorm → [[runbooks/brainstorm]] · agent → [[runbooks/agent-operator]] · repo → [[runbooks/repo-commit]] · project → [[runbooks/project]] · skill → [[runbooks/skill]] · entity-слой → [[runbooks/entity]] · sched → [[runbooks/sched-telemetry]]
**Methodology:** `methodology/kzntsv`
**Canon version:** 4
<!-- /mappa:canon-block -->

View File

@@ -122,6 +122,8 @@ an explicit `adapted-from` marker in its frontmatter.
| `code-review` | `adapted-from: mattpocock/skills @ 84fdeffd` (MIT) — two-axis + Fowler baseline; output: caveman-review format |
| `writing-skills` | `adapted-from: obra/superpowers @ 6.2.0` (MIT) — TDD-for-skills core + ideya 8 self-skill-authoring |
| `web-search` | `author: ours` — search_web tool (pi-extension) + policy: when to search, «без поиска» session-off |
| `ops-browser` | `author: ours` — свой **скрытый** браузер агента: отдельный профиль + CDP (`eval`/`fetch` из страницы/скриншоты), `handoff` человеку для пароля/капчи; свой замок `ops.lock` |
| `browser-operator` | `author: ours` — браузер ОПЕРАТОРА (его Chrome/логины): канал по харнессу (Hermes `browser_exec` / pi тул `browser` / CC `chrome-devtools`), аренда «один водитель за раз», границы «человек vs агент», рецепты тяжёлых страниц. Закрывает провал базового прогона 2026-09-11 («куки из Chrome + curl + ввод пароля» мимо канала); анонимные прогоны — `browser-cdp` |
| `review-subagent` | `author: ours` — review_subagent tool (pi-extension): clean-context review by your own model, optional `model` override |
| `report-mappa-issue` | `author: ours` — TEMPORARY stopgap: mappa deviation reporting (mail to `mappa` + `.workshop`) while the service is unstable; retire when stabilized |
| all other `skills/*` | `author: ours` |

View File

@@ -91,6 +91,8 @@ bash scripts/build.sh caveman # один
| `code-search` | `author: ours` — rg-first код-поиск (замер: 15 мин → 0 сек; роутинг: rg / git grep / interns repo_read / grep_audit) |
| `code-review` | `adapted-from: mattpocock/skills @ 84fdeffd` (MIT) — двухосевость + Fowler-база; формат вывода: caveman-review |
| `writing-skills` | `adapted-from: obra/superpowers @ 6.2.0` (MIT) — TDD-for-skills ядро + идея 8 self-skill-authoring |
| `ops-browser` | `author: ours` — свой скрытый браузер агента (профиль + CDP + `handoff` человеку, замок `ops.lock`) |
| `browser-operator` | `author: ours` — браузер ОПЕРАТОРА (его Chrome/логины): канал по харнессу (Hermes `browser_exec` / pi тул `browser` / CC `chrome-devtools`), аренда «один водитель за раз», границы «человек vs агент»; анонимные прогоны — `browser-cdp` |
| остальные `skills/*` | `author: ours` |
Политика адаптации: клон переписывается под наши конвенции (доски `.tasks/`,

BIN
dist/browser-cdp.skill vendored

Binary file not shown.

BIN
dist/browser-operator.skill vendored Normal file

Binary file not shown.

BIN
dist/ops-browser.skill vendored Normal file

Binary file not shown.

View File

@@ -55,6 +55,14 @@ skills:
mode: auto
category: software-development
browser-operator:
mode: auto
category: software-development
ops-browser:
mode: auto
category: software-development
using-markitdown:
mode: auto
category: productivity

View File

@@ -1,13 +1,14 @@
---
name: browser-cdp
author: ours
version: 0.1.0
version: 0.1.1
description: >
Веб-автоматизация через минимальные CDP CLI-тулы в bash — вместо playwright-mcp
/ Chrome-DevTools-MCP (подход «what if you don't need MCP»). Запуск Chrome с remote
debugging, навигация, eval JS, скриншоты. Trigger: «браузер», «скрейпинг», «открой
страницу», «перейди на», «сделай скриншот», «playwright», «веб-автоматизация»,
«web scraping», «browser».
«web scraping», «browser». Для ЛИЧНЫХ КАБИНЕТОВ оператора (его логины, антибот) —
НЕ этот скил, а `browser-operator`.
---
# browser-cdp
@@ -20,6 +21,9 @@ description: >
снять скриншот, собрать данные (скрейпинг). Использовать **вместо** playwright-mcp или
Chrome-DevTools-MCP.
- ⚠️ **Для личных кабинетов оператора этот путь НЕ годится:** здесь свой Chrome и свой
профиль (без его логинов). Нужен браузер оператора — скил `browser-operator`.
## Процесс
1. **Прочитай полную справку** (обязательно, первый шаг):

View File

@@ -0,0 +1,128 @@
---
name: browser-operator
author: ours
version: 0.1.0
description: >
Работа в браузере ОПЕРАТОРА — его Chrome, его профиль, его живые логины: личные
кабинеты поставщиков (ЧипДип, ДКО, Промэлектроника, ТМ), Avito, порталы под
антиботом. Trigger: «зайди в личный кабинет», «открой ЛК», «собери заказы»,
«посмотри в браузере», «нужна его сессия», «ЧипДип/Ozon/Avito», «browser».
НЕ для публичных страниц (там сначала обычный fetch/curl) и НЕ для анонимных
прогонов (там скил browser-cdp).
---
# Browser operator — браузер оператора
**Браузер оператора — разделяемый ресурс с его живыми сессиями.** Работай через
готовый канал своего харнесса и **только под арендой «один водитель за раз»**.
Не вытаскивай его куки, не логинься сам, не поднимай свой Chrome.
## Когда браузер действительно нужен
Сначала спроси себя, нужен ли браузер вообще:
- **Нет** — публичная страница, API, доки: обычный fetch/curl/поиск. Браузер тут лишний.
- **Да** — страница требует взаимодействия (клик/форма/навигация), JS-рендер,
**его логин** (личный кабинет, Avito), либо портал режет не-браузерный трафик.
## Канал по харнессу (не изобретай свой)
| Харнесс | Чем работать | Аренда |
|---|---|---|
| **Hermes** | тул `browser_exec` (демон `browser-use`, тот же профиль оператора) | автоматически: shell-хук `pre_tool_call` берёт аренду и блокирует вызов при чужой |
| **pi** | тул `browser`: `open` / `read` / `js` / `click` / `screenshot` / `lease` | автоматически внутри тула; `lease` покажет, кто держит |
| **Claude Code** | MCP `chrome-devtools` (`list_pages`, `navigate_page`, `take_snapshot`, `evaluate_script`, `click`, `fill`, `take_screenshot`) | автоматически: обёртка берёт аренду перед стартом сервера |
**Никогда:** `curl` с куками из его профиля, `browser_cookie3`-выгрузка куки,
свой `chrome --remote-debugging-port` с пустым профилем (там нет его логинов),
`pip install`-стек ради одного кабинета.
## Аренда: «один водитель в браузере за раз»
Браузер один. Если его держит другой харнесс — **не ломиться**, сказать «занято,
держит X» и вернуться позже (или попросить оператора освободить).
Идёшь в браузер **в обход** тула (например, скриптом или `browser-use` из терминала) —
бери аренду сам:
```bash
BL="$HOME/.config/browser-harness/bin/browser-lease.sh"
HOLD="manual:$$" # метка держателя
MYPID="$(cat /proc/$$/winpid 2>/dev/null || echo 0)" # НАСТОЯЩИЙ windows-PID
bash "$BL" acquire --holder "$HOLD" --ttl 900 --pid "$MYPID" --tool browser # 0 = взял, 3 = занято, 2 = ошибка
# … работа …
bash "$BL" release --holder "$HOLD"
```
**PID — только настоящий.** MSYS `$$` это НЕ windows-PID: по нему живость аренды врёт
(мёртвый держатель выглядит живым, живой — мёртвым). В bash бери `/proc/$$/winpid`,
в pi/Node — `process.pid`; не знаешь — передай `0` («неизвестен», живость решит TTL).
**`driver.lock` руками не трогай** (в том числе пустой или «битый» — это окно чужой
записи): отбор мёртвой аренды делает CLI по `ts`/TTL/мёртвому PID. Чужую аренду не снимай.
Контракт аренды (формат файла, TTL, кого связывать): вики mappa
`concepts/browser-lease-contract`.
## Как работать в страницах (рецепты)
- **Первым делом — своя вкладка.** `ensure_real_tab()` / `new_tab(url)`; не полагайся
на активную вкладку оператора: тяжёлая или аудио-страница подвешивает демон
(все вызовы падают в таймаут, хотя `browser-use --doctor` говорит «alive»).
- **Тяжёлые страницы — читать изнутри, а не обходом.** Если у сайта есть внутренний
JSON-эндпоинт, зови его `fetch(path, {credentials:'include'})` из уже открытой
страницы: это быстрее и надёжнее десятков навигаций.
- **По одной штуке за раз, с паузами.** Паузы — в Python/процессе, **не** в JS `await`
(иначе `Runtime.evaluate timed out`). Ориентир оператора: 1 запрос, пауза 713 с,
перекур каждые ~40, стоп после 3 ошибок подряд.
- **Клики:** сначала дерево доступности (`Accessibility.getFullAXTree`) или
`querySelector` → центр элемента (`getBoundingClientRect`) → `click_at_xy` → **проверь
результат** отдельным `js(...)`/`page_info()`. Не кликай «на глаз» по скриншоту.
- **Прогресс — сразу на диск** (jsonl/atomic), а не в конце прогона: длинные обходы
обрываются.
- **Долгие обходы** — в фоновый процесс, а не в цикл интерактивных вызовов.
## Границы: где человек, а где агент
- **Пароли и второй фактор — никогда.** Не вводим и не просим в аргументах командной
строки. Уже залогиненная сессия оператора — вот твой доступ.
- **Попап «Разрешить удалённую отладку?»** — это человеческое подтверждение: агент его
не жмёт, а просит оператора нажать «Разрешить» и повторяет попытку.
- **Деньги/платежи/отправка форм с персональными данными** — только с явным
подтверждением оператора.
- **Секреты и содержимое залогиненных страниц** не пишем в логи, файлы репозитория и
вики: в mappa — только агрегаты и идентификаторы.
## Проверенные адреса и факты
- **ЧипДип, кабинет заказов:** `https://www.chipdip.ru/order/list`
(⚠️ `/cabinet` и `/orders` отдают 404 — не перебирай наугад, ссылка есть в шапке).
- Залогиненность видна в шапке кабинета (имя оператора); если видишь форму логина —
**сессия потеряна: стоп и скажи оператору**, не логинься сам.
- `about:blank`-вкладка с титулом-лошадкой в `document.title` = страницу ведёт агент.
## Так делать НЕ надо
| Соблазн | Почему нет |
|---|---|
| «Вытащу куки из Chrome и пойду `curl`'ом» | мимо канала и мимо аренды; пароль/2FA оказываются рядом; ломается при смене защиты |
| «Подниму свой Chrome с отладкой» | там **нет** его логинов → выкинет на логин/капчу, плюс это второй водитель |
| «Введу логин/пароль через `read -s`» | пароли и 2FA не вводим никогда |
| «Проверю ещё пяток URL кабинета» | адрес подтверждай по ссылке в интерфейсе, а не перебором |
| «Налью 20 запросов параллельно, быстрее» | антибот + оператор останавливает такие прогоны; один водитель, одна очередь |
Основание: базовый прогон без скила (2026-09-11) ушёл именно в «куки + curl + пароль»
мимо канала; скил закрывает этот путь.
## Красные флаги (стоп и перечитай)
- Собираешься вытащить куки / поднять свой браузер / ввести пароль или код.
- Работаешь с браузером **без** аренды (или ломишься, когда «занято»).
- Полливаешь запросами без паузы или ждёшь паузу в JS.
- Пишешь содержимое залогиненных страниц/секреты в файл, лог или вики.
## Вне скоупа
- Анонимные/антидетект-прогоны, свой профиль, `mode: fresh` — скил `browser-cdp`.
- Облачные браузеры (Browser Use Cloud и прочие) — не берём.
- Свой stdio-MCP-сервер поверх общего демона — отдельная тема.

View File

@@ -0,0 +1,90 @@
---
name: ops-browser
author: ours
version: 0.1.0
description: >
Use when a task needs a real browser — личный кабинет, заказы, чеки, yt-digest,
скриншот живого сайта, «дёрни внутренний API». Свой СКРЫТЫЙ браузер агента:
отдельный профиль, CDP, `fetch()` ИЗ страницы, хендофф человеку для пароля/капчи.
---
# ops-browser — свой скрытый браузер агента
**Есть задача, которой нужен браузер? Работай в своём браузере, скрыто, и не подсовывай куки.**
Профиль владеет сессией сам (куки продлеваются браузером, а не руками) — это и есть лечение
боли «куки протухли».
## Когда ЭТОТ инструмент, а когда другой
| Ситуация | Чем работать |
|---|---|
| Публичная страница без логина | `web_search` / `web_extract` / curl — браузер не нужен |
| Личный кабинет, заказы, чеки, внутренний API сайта, антибот | **`ops-browser.sh`** (этот скил) |
| Нужны ЕГО логины и ЕГО Chrome (Avito, кабинеты поставщиков) | канал оператора: Hermes `browser_exec` / pi тул `browser` / CC `chrome-devtools` — всё под арендой `driver.lock` |
| Посмотреть глазами, кликнуть по живому сайту, показать ему | панель предпросмотра (`desktop_preview` + `drive_preview`) — без JS, только текст/клики |
`ops-browser` не заменяет канал оператора: там его сессии, здесь — **мой** профиль.
И то и другое живёт под правилом «один водитель» (у ops свой замок `ops.lock`).
## Команды
```bash
OB="$HOME/.config/browser-harness/bin/ops-browser.sh"
bash "$OB" start # headless + аренда (окон нет)
bash "$OB" open https://site/path # перейти
bash "$OB" read 2000 # видимый текст страницы
bash "$OB" eval 'fetch("/api/x",{credentials:"include"}).then(r=>r.json()).then(j=>JSON.stringify(j))'
bash "$OB" screenshot [path] # PNG, печатает путь
bash "$OB" cookies https://site # имена кук (без значений)
bash "$OB" status # аренда + CDP + текущая страница
bash "$OB" stop # ШТАТНО закрыть и отпустить
bash "$OB" handoff <url> # человеку: пароль/2FA/капча (см. ниже)
```
`eval` выполняет JS **в контексте страницы** — значит `fetch()` идёт с её куками и заголовками:
так дёргают внутренние эндпоинты приложения (то, что недоступно извне и чего не умеет панель).
## Хендофф человеку (пароль, 2FA, капча, согласие)
Останавливаюсь и отдаю экран **сам**, без «скажи готово»:
1. `bash "$OB" handoff <url>` — закрывает headless (профиль нельзя открыть дважды) и поднимает
**видимое** окно ТОГО ЖЕ профиля на нужной странице;
2. человек вводит, что нужно (пароль/2FA/капчу вводит только он);
3. признак успеха ловлю сам: URL ушёл со страницы логина/auth **или** выросли куки домена;
4. штатно закрываю окно и возвращаюсь в headless — логин остался в профиле.
Скриншот делаю **до** шага (что от человека хотят) и **после** (что получилось); **во время ввода
пароля не снимаю**. Если признак успеха не увидел — окно остаётся открытым, а я говорю об этом прямо.
## Границы (не двигаются)
- Пароли, 2FA, капчу — **вводит человек**. Я к ним не подхожу.
- Деньги, оформление заказов, формы с перс.данными — только с явного согласия оператора.
- Куки не выгружаю и не расшифровываю; содержимое залогиненных страниц не уходит в логи/вику.
- Антибот-челленджи не обхожу; если сайт невзлюбил `headless` — это повод для `handoff`, а не для трюков.
## Грабли (стоили времени, проверены живьём)
- **Закрывать только штатно** (`stop``Browser.close`). Жёсткий kill может не сбросить профиль →
потеря логина. Сессионные куки (без срока) перезапуск не переживают по дизайну — настоящие логины живут.
- **MSYS-пути**: нативным программам нужен вид `C:/...` (`cygpath -m`), а `powershell -File`
только `C:\...` (`cygpath -w`). Иначе «модуль не найден» / «файл .ps1 не найден».
- **Экранирование PowerShell внутри bash-строки тихо ломается** (`$_` подменяется) — PS-запросы
держим в отдельных `.ps1`.
- **Профиль нельзя открыть дважды** — перед видимым окном headless обязан остановиться (это делает `handoff`).
- **Профиль**: `~/.config/browser-harness/profiles/ops`; порт CDP: `OPS_CDP_PORT` (9346).
## Аренда
```bash
BL="$HOME/.config/browser-harness/bin/browser-lease.sh"
bash "$BL" --lock ops status # состояние ops-замка (у Chrome оператора — driver.lock)
```
Чужой держатель `ops.lock``start` честно отказывает (rc=1). Мёртвый держатель отпускается по PID/TTL.
Идёшь в браузер **сам** (в обход `ops-browser.sh`)? Возьми `ops.lock` так же, как любой другой харнесс.
Контракт: mappa `concepts/ops-browser-contract` (проект `pi-extensions`),
требования `requirements:42`, план `plan:35`. Исходники: репо `pi-extensions/scripts/browser/`
(установка `just install-browser`).