---
name: fb-watch
description: "Мониторит стену Facebook Антона на АВТОРСКИЕ посты, к которым ещё НЕТ тизера, и сразу пишет тизеры (RU→@ClawRus, EN→X) — draft-first, пока не взведён авто-постинг. Trigger on “/fb-watch“, “проверь фейсбук на тизеры“, “посты без тизера“, “добей тизеры“, “fb watch“, “check facebook for un-teased posts“"
version: 1.0.0
---

# /fb-watch — добиваем тизеры на FB-посты Антона

> ⛔ **ЛИЧНОСТЬ БРАУЗЕРА (канон 30.08.2026).** Chrome профиля Default на хабе = личность Антона (claude.ai там под **[рабочий аккаунт]**, всегда).
> Робот здесь **ЧИТАЕТ и действует, но НЕ ТРОГАЕТ ЛОГИНЫ**: ни logout, ни смены аккаунта, ни «самолечения входа», ни инжекта кук.
> Увидел «не тот аккаунт» → доложи и работай из робо-профиля (`_chrome_profile_a2`, Firefox-профили). Канон: `reglament-hab-brauzernye-lichnosti-bb-i-roboty`.

> **Зачем.** Антон иногда пишет пост ПРЯМО на стену FB, минуя пайплайн (голос→эпизод→тиры).
> Такой пост остаётся БЕЗ тизера в @ClawRus(RU)/X(EN) → охват теряется. Этот сторож ловит
> «есть авторский FB-пост, тизера ещё не было» и сразу пишет тизеры. Draft-first, пока не взведён.

## Инструменты (тонкий оркестратор, AK-47)
- **Чтение стены** = Claude-in-Chrome MCP (живая залогиненная вкладка FB, локально на хабе). Реюз паттерна из `/fb-reply` (in-page `javascript_tool`, FB прячет имена за MCP-границей).
  - ⛔ **НЕ читай ленту профиля `/AntonyDzi`** (консенсус #0223a74a, 20.07): она виртуализирована и рендерит ОДИН пост — соседи висят вечными скелетонами, `scrollHeight` константа, реальный скролл не помогает. Один прогон так спрятал 2 контентных поста из 5.
  - ✅ **Рельс жатвы = Content Library** `https://www.facebook.com/professional_dashboard/content/content_library/`. ⛔ ПАРС РУКАМИ НЕ ПИСАТЬ: он живёт в файле `~/.claude/scripts/fb_harvest_content_library.js` — вставь его содержимое в `javascript_tool` целиком. Проза про парс дважды разъезжалась с разметкой (05.08: «якорь Published, текст = строка перед» не соответствовал DOM).
  - ✅ **PERMALINK РЕШЁН (05.08.2026), hover по стене больше НЕ НУЖЕН.** У каждой строки таблицы ссылка `/content/insights/?content_id=<base64>`, и base64 декодируется в `S:_I<user_id>:<story_fbid>:<story_fbid>` → `https://www.facebook.com/<user_id>/posts/<story_fbid>`. Отсюда же берётся **story_fbid** — сильнейший ключ дедупа (pfbid ротирует, он не идентичность). Сторис отличаются по типу объекта: `S:_ISC:<id>` (⛔ не по тексту-плейсхолдеру «Photo story»: он одинаковый у всех сторис → одинаковый text_hash → дедуп склеил бы их в одну).
  - ⚠️ **Три грабли транспорта, замерены 05.08** (не переоткрывать): (1) Chrome даёт **ОДНО** авто-скачивание на вкладку — второе молча не происходит, на диске остаётся вчерашний файл ⇒ жать в СВЕЖЕЙ вкладке и качать РОВНО раз; (2) `fetch` на `127.0.0.1` из facebook.com режет CSP — локальный приёмник не работает; (3) `mbasic.facebook.com` мёртв, редиректит на www.
  - ⚠️ **Таблица догружается только РЕАЛЬНЫМ скроллом** (`computer scroll`, ~4 круга по 10-35 тиков): JS-скролл контейнера даёт максимум 10 строк и застревает, в фоновой вкладке FB вообще не дорисовывает. Признак недобора — в жатве меньше ~10 постов.
  - **АПГРЕЙД (Антон одобрил 2026-06-30, ждёт креды):** как только есть `secrets\fb_graph.env` (FB_USER_TOKEN, `user_posts`) и `python ~/.claude/scripts/fb_posts_poll.py check` зелёный → читай стену через `python ~/.claude/scripts/fb_posts_poll.py posts --limit 10 --out posts.json` (Graph API, надёжнее браузера, без риска бана) ВМЕСТО Claude-in-Chrome. См. [[decision-social-posting-stack]] §6.
- **Детектор/леджер/кап** = `python ~/.claude/scripts/fb_teaser_watch.py` (0 токенов, дедуп + дневной кап + kill-switch). Состояние: `$IMPORTS_ROOT/content-factory/fb_teaser_ledger.json` + `fb_watch_config.json`.
- **Тизеры** пишет Opus в сессии (голос Антона), палитры: `_STYLE.md`/`_CRAFT.md` + `style-reality-show.md`. Черновики → `_imports\content-factory\fb-teasers\<id>.md`.
- **Постинг RU** = Telegram MCP `send_message`, аккаунт **tonydzi** (создатель), чат **-[id]** (@ClawRus). **EN/X** = пока нет постера → ВСЕГДА черновик (ждёт social-tools DR).

## Процедура
1. **Готовность Chrome.** Проверь живую вкладку FB (Claude-in-Chrome). Не готов/не залогинен → **флагни в чат 03** («fb-watch: Chrome/FB не готов, стену не прочитал») и СТОП. Не делать вид, что «постов нет» (слой видимости — тихий ноль = поломка).
2. **Жатва (механическая, без импровизации):** открой СВЕЖУЮ вкладку → Content Library → вставь `~/.claude/scripts/fb_harvest_content_library.js` в `javascript_tool` (он ждёт прорисовку, копит строки в `window.__fbacc`, декодирует `content_id` → story_fbid + permalink, схлопывает задвоенный текст и скачивает сырьё) → 3-4 круга `computer scroll` по таблице → финальный `__fbgrab()` + одно скачивание.
   Текст берётся из строки таблицы ПОЛНОСТЬЮ, разворачивать «See more» не нужно.
3. **Нормализация:** `python ~/.claude/scripts/fb_cl_normalize.py` (сам берёт свежайший `fb_content_library_scan*.json` из Downloads, ругается на протухший вход старше 45 мин, отсекает сторис/дубли/пустой текст, пишет `<CF>\fb_posts_scan.json`).
   **Детектор:** `python fb_teaser_watch.py unteased --in "<CF>\fb_posts_scan.json"`. На выходе — посты БЕЗ тизера.
   🚨 **exit 4 = `HARVEST-THIN`** (собрано <3 постов, порог `FB_TEASER_MIN_HARVEST`): это НЕ «постов нет», это СБОЙ ЖАТВЫ → пережни через Content Library и флагни в 03. Тихий ноль запрещён (консенсус #0223a74a). **Правило: 1 тизер на КАЖДЫЙ такой пост** (нашёл 5 — пиши 5; это не дневной кап).
   🛡️ **Freshness-гард (14.07):** незнакомый пост старше 72ч (`--max-age-hours`) движок сам засевает как skip со stable-ключами — бэклог стены и ротация pfbid флуд дать не могут; в stderr это `stale-auto-skipped=N`. Отключается `--no-auto-skip` (не отключай в рутине).
   - **FORWARD-ONLY первый прогон:** `python fb_teaser_watch.py backfill-status` → если NOT DONE (exit 1) и список длинный (это весь бэклог стены) → НЕ публикуй массой: пометь каждый существующий пост `seed-skip --id <id>`, затем `mark-backfill-done`, доложи «forward-only: N постов засеяно как skip, с этого момента каждый НОВЫЙ пост получает тизер». Дальше — только реально новые посты.
4. **Для каждого un-teased поста** напиши 2 тизера (Opus, голос Антона) **строго по плейбуку** `08-Templates\teaser-writing-playbook.md` (крючок в 1-ю строку, ОДИН приём, 4U-чек, клиффхэнгер + «полное → [ссылка на FB-пост]»):
   - **RU** — тёплый, для @ClawRus, **240–370 знаков (HARD)**.
   - **EN** — для X, **≤280 знаков**.
   - Приватность HARD: ни чужих имён/@/сумм/секретов. CTA co-founder тут НЕ нужен (он в среднем/лонге). Сохрани оба в `fb-teasers\<id>.md`.
5. **Распубликуй:**
   - **RU:** `python fb_teaser_watch.py can-post --rail ru` → exit 0 (взведён + под капом) → запость текст в @ClawRus (Telegram MCP, tonydzi, chat -[id]) → `mark-posted --id <id> --rail ru`. Exit 3 (disarmed/кап) → оставь черновиком.
   - **EN:** `x_poster_ready` в `fb_watch_config.json` решает. `false` → черновик. `true` → `python ~/.claude/scripts/x_post.py post "<en teaser>"` (X API v2, OAuth1; exit 0 = запостил, печатает url). Постер взводится, когда у Антона есть X dev-креды в `secrets\x_api.env` и `x_post.py check` зелёный (см. [[decision-social-posting-stack]] §6).
   - Затем `python fb_teaser_watch.py record-draft --id <id>` (чтобы пост не всплывал снова).
   - **ТЕМП (05.08):** `min_gap_min` в конфиге (дефолт 12) — минимум между RU-постами; `can-post` теперь его проверяет. Антон пишет по 10-15 постов в день, взведённый конвейер иначе вывалит пачку в канал за минуту, а 11 разных тизеров подряд читаются как спам так же, как один веером. Пачку больше двух разноси отложенной отправкой с шагом 12-20 мин.
6. **Доложи Антону.** СТАТУС-ОТЧЁТ (сколько найдено, что запостил RU, что в черновике) — это отчёт, не вопрос → в чат 03 / TG-папку, как обычно.
   **Если нужен ЕГО ОК** (режим проверки/disarmed: «публикуем И взвести авто?») — ⛔ НЕ пиши вопрос в 03 руками (там heartbeat-шум хоронит аски, Антон не увидит). Подними аск через движок remote-approval, который сам шлёт в **02-POLICE ПЕРВЫМ** + дублирует в 03/личку:
   `python ~/.claude/scripts/approval.py ask "fb-watch: N черновиков готовы. Публикуем И взводим авто-arm? QQQ=да,публикуем+arm · NO=оставить черновиками"` → вернёт `{id, ask_text, targets}`. Затем ОТПРАВЬ `ask_text` по `targets` **строго по порядку** (Telegram MCP; targets[0] = 02-POLICE = первым). Проверка ответа: `python ~/.claude/scripts/approval.py check`. Канон [[remote-approval-qqq]] («QQQ-аск → 02-POLICE first»); гейт `lint_approval_routing.py` следит, чтоб этот шаг не разъехался снова.

## Шаг 2-бис — КОММЕНТ-КОНВЕЙЕР (anton 31.07: «это должна быть рутина»)
Тот же дневной прогон, та же живая вкладка FB — вторым проходом собираем КОММЕНТЫ к последним ~5 авторским постам:
1. **Жатва комментов** (та же вкладка, in-page JS как в `/fb-reply`): по каждому посту `{автор, текст, ссылка, наш ответ уже есть?}`. Дедуп-леджер: `$IMPORTS_ROOT/content-factory/fb_comments_ledger.json` (ключ = hash(пост+автор+первые 60 знаков); обработанный коммент второй раз не всплывает).
2. **Классификация** = 4 корзины `/fb-alpha` (артефакт/лид/обещание/шум). Артефакты → тест по `/fb-alpha` шаг 3; обещания → задача `10-Tasks`.
3. **Дерзкие ответы** = режим §2 `/fb-reply` (ДЕФОЛТ anton 31.07): на КАЖДЫЙ содержательный коммент 2-3 слова (потолок 5-7), уникально, ban-лист ai-slop. Постим тут же с хаба (тред СВОЕГО поста = класс C, «+» не ждём; плашка Майкрофта в треде своего поста не вешается). Темп `fb_guard.py check reply` (≤40/день ≥5мин) — красный → остаток в черновики.
   ⚠️ НЕ путать с уроком `/comments` 15.07 («молчать, ≤3») — тот про СТАРЫЕ треды в ЧУЖИХ группах; здесь свежие комменты под СВОИМ постом, отвечаем всем содержательным.
4. **DM-конвейер** = §Конвейер `/local-fb-messenger` (разузнать→знакомство/touch base→@clawrus→звонок AI-практикам). DM = Tier-2 draft-first: пачка черновиков → аск через `approval.py ask` в 02 → отправка по «+», по одному, с плашкой Майкрофта.
5. **Отчёт** строкой в 03: «💬 комментов N · ответил K · артефактов X · DM-черновиков M ждут "+"».
Хаб-only (IP-гейт): жатва+ответы только с хаба; [машина флота]/пиры могут гнать шаги 2 и 4-черновики по присланному дампу.

## Границы (Tier-2 / безопасность)
- **Авторизация Антона (2026-06-30):** RU-авто-постинг в @ClawRus **ВЗВЕДЁН** (`armed=true`) — Антон дал стоячую авторизацию (тизер = указатель на уже публичный FB-пост, риск минимальный). Kill-switch: `disarm` в любой момент.
- **1 тизер на каждый пост без тизера** (НЕ дневная квота). Дедуп по леджеру → двойного поста не будет. `daily_hard_cap` (25) — только страховка от бага-флуда.
- **Forward-only:** первый взведённый прогон засевает существующую стену как `skip` и НЕ постит — чтобы не залить @ClawRus бэклогом; дальше тизер получает каждый НОВЫЙ пост.
- Только ЕГО собственные посты и его собственные каналы. Никакого блайнд-автопоста в чужое.
- Авторский голос = **только Opus**. Грунт (детектор) = 0 токенов.
- Браузерная работа строго ЛОКАЛЬНО на хабе ([[browser-work-on-peers-not-hub]]).

## Связь
Канон правила: память [[teaser-crosspost-clawrus]] + `reglament-tizery-krosspost-v-clawrus-i-tg`. Родня: [[fb-skill-set]] (/fb-post, /fb-reply, fb_guard), [[content-factory]] (Distribute-стадия), [[short-text-when-unreviewed]]. Рутина-двойник: scheduled-task `fb-watch-daily` (1×/день днём).
