---
name: get-tg-jobs
description: Full autonomous end-to-end job pipeline. Fetches fresh vacancies from Oleg's Telegram "Jobs" folder, handles new/pending channels, runs the full triage cycle (triage-jobs), pushes APPLY verdicts to Trello, then reports results in the chat session AND sends a Telegram notification to Oleg via telegram-helper. Trigger on "забери из Jobs", "что нового в каналах", "запусти pipeline", or similar.
---

# get-tg-jobs

Autonomous end-to-end pipeline: fetch → curate new channels → triage → Trello → report. Does everything without stopping for confirmation. Only halts if a hard error occurs (auth failure, script crash, etc.).

**Trigger only when Oleg explicitly asks** — e.g. "забери свежее из Jobs", "что нового в каналах", "запусти pipeline". No background polling.

---

## Notification targets

All pipeline reports go to the **"New cool job"** Telegram group. The `telegram-helper` account (samuishechka) is a member of that group and sends messages there.

| Target | chat_id | When |
|---|---|---|
| "New cool job" group | `-4783500394` | All reports — success and errors |

Tool to use: `mcp__telegram-helper__send_message(chat_id=-4783500394, message="...", parse_mode="html")`

This section is the canonical reference for notification targets. Update here if the group changes.

---

## Step 1 — Run the pipeline

```bash
~/.local/bin/uv run scripts/list_telegram_channels.py \
  | ~/.local/bin/uv run scripts/fetch_telegram_jobs.py -
```

Run from the project root. The script prints a per-channel summary to stdout — echo it to the session so Oleg can see raw progress.

**If the script fails:**
- `TELEGRAM_SESSION_STRING not set` or auth error → `.env` is missing or the session is expired. Regenerate with `scripts/regen_telegram_session.sh` and tell Oleg.
- `TELEGRAM_API_ID / TELEGRAM_API_HASH not set` → same `.env` issue.
- `Could not find folder "Jobs"` → the folder was renamed or the wrong session is in `.env`. Must be the **usulsu** session, not samuishechka.
- Any other exception → paste the error and stop. Don't retry blindly.

---

## Step 2 — Check for pending (new) channels

```bash
test -f tracking/telegram_pending_channels.json && echo PENDING || echo OK
```

**If OK**: skip to Step 3.

**If PENDING**: curate each new channel using the procedure below, then rerun Step 1.

---

### New-channel curation procedure

For each channel in `telegram_pending_channels.json`:

#### 2a — Read keyword frequency scan

```bash
jq -r 'to_entries[] | "\n=== \(.key) ===\nmessages_scanned: \(.value.messages_scanned)\ntruncated: \(.value.truncated)\ntop keywords:\n\(.value.keyword_counts_from_other_channels | to_entries | sort_by(-.value) | .[:20] | .[] | "  \(.key): \(.value)")"' \
  tracking/telegram_pending_channels.json
```

#### 2b — Sample messages

```bash
jq -r '.channels["<channel>"].messages[:6] | .[] | "── \(.date[0:16])\n\(.text[0:400])\n"' \
  tracking/telegram_inbox.json
```

Look for: hashtag patterns, language, post structure (single role / digest / chat), recurring noise.

#### 2c — Decide lang, priority, filter shape

**Priority:**
- `p1` — strong stack hits (JS/TS/React/Node/AI/LLM) AND global-remote culture
- `p2` — stack OK but CIS/RUB market, or occasional gems; recruiter/market-intel content
- `p3` — wrong stack, off-market, noise, dead channels

Base priority on the **best** vacancy in the sample, not the average.

**Filter shape:**

| Channel pattern | Filter |
|---|---|
| Consistent `#vacancy` + `#remote` hashtags | AND-of-OR hashtag filter |
| Vacancy text without consistent hashtags | Positive stack include + Oleg-stack excludes |
| Low-volume personal/curated digest | Trust-all (no `include`/`exclude`) |
| Mixes resumes and vacancies | Trust-all |
| Mostly noise but worth keeping | Strict positive filter |

Standard Oleg-stack excludes: `["kafka", "golang", "kotlin", "android", "swift"]`

For `*_jobs` channels add resume excludes: `["#резюме", "#resume", "#cv", "#ищуработу"]`

Positive stack include: `["javascript", "typescript", " react", "node.js", "nodejs", "fullstack", "full-stack", "tech lead", "techlead", "ai engineer", " llm"]`

#### 2d — Add entry to telegram_channels.json

Insert in correct priority block (p1 → p2 → p3, alphabetically within each group):

```jsonc
"<channel_id>": {
  "lang": "ru" | "en" | "mixed",
  "priority": "p1" | "p2" | "p3",
  "include": [["kw1","kw2"], ["kw3"]],  // optional
  "exclude": ["kw4", "kw5"]              // optional
}
```

#### 2e — Rerun and confirm clean

```bash
~/.local/bin/uv run scripts/list_telegram_channels.py \
  | ~/.local/bin/uv run scripts/fetch_telegram_jobs.py -

test -f tracking/telegram_pending_channels.json && echo "STILL PENDING" || echo "Clean"
```

#### 2f — Validate the filter

```bash
jq -r '.channels["<channel>"] | "kept \(.kept)/\(.seen)" + (.messages | map("\n── \(.date[0:16])\n\(.text[0:300])") | join(""))' \
  tracking/telegram_inbox.json
```

If `kept == 0` and a filter is set — verify the filter isn't too strict before accepting zero as correct.

---

## Step 3 — Run full triage

Follow the complete `triage-jobs` skill procedure on `tracking/telegram_inbox.json`.

Key points from that skill:

1. **Pre-step**: build `known_applied` list before spawning Haiku:
   ```bash
   grep -E '^\|' tracking/applications.md | tail -n +2 | awk -F'|' '{print $3}' | sort -u
   ```
   Also fetch Trello card titles from all BestJob board lists. Pass the merged list into every Haiku subagent prompt.

2. **Stratify by priority**: all p1, p2, p3 go through Haiku first pass. Main session reviews only APPLY and VERIFY verdicts from Haiku. SKIP verdicts from Haiku accepted without re-review.

3. **VERIFY verdicts**: run a fast `WebSearch "{company} remote hiring countries"` to resolve geo, then apply Gate 1.

4. **APPLY verdicts**: for every confirmed APPLY —
   - Add a row to `tracking/applications.md` with status `todo`
   - Create a Trello card in the TODO column (`6a1aa59555aab72a261c42aa`) on board `6a1a9a5af082cb0526b22704` with: company, role, track, verdict summary, JD link

5. **QUICK-APPLY verdicts**: note them explicitly in the session report; do not auto-add to Trello. Ask Oleg if he wants to proceed.

Full triage playbook: `base/reference/vacancy-filter-and-triage-2026.md`. Full batch instructions: `.claude/skills/triage-jobs/SKILL.md`.

---

## Step 4 — Report in session

After triage is complete, output a consolidated summary to the chat:

```
Pipeline done — <date>

Fetch: N messages from X channels (Y with results)
[⚠️ Truncated: channel_name (kept K/500, p2/ru)]

Triage results:
  ✅ APPLY: N roles → added to Trello
  🔍 VERIFY: N roles → resolved / still open
  ⚡ QUICK-APPLY: N roles (ask Oleg before adding)
  ❌ SKIP: N roles

APPLY roles:
  - Company A — Role Title (Track A) → [Trello card]
  - Company B — Role Title (Track B) → [Trello card]

New channels curated: N [or: none]
```

---

## Step 5 — Send Telegram notification

After the session report, send a summary to the **"New cool job"** group via telegram-helper.

### Success message format

```
mcp__telegram-helper__send_message(
  chat_id=-4783500394,
  parse_mode="html",
  message="""<b>Jobs pipeline done</b> · 2026-06-07

✅ APPLY: N роли → Trello
🔍 VERIFY: N (нужен ресёрч)
⚡ Quick-apply: N (нужно ок)
❌ SKIP: N
📥 Всего: N сообщений из X каналов

<b>APPLY:</b>
– <a href="https://trello.com/c/...">Company A — Role (Track A)</a>
– <a href="https://trello.com/c/...">Company B — Role (Track B)</a>"""
)
```

Rules:
- Always include Trello links for APPLY roles — это главная ценность уведомления.
- If QUICK-APPLY roles exist, list them with direct JD links (not Trello — для быстрого решения):
  ```
  ⚡ Quick-apply (нужно ок):
  – Company X — Role → wantapply.com/...
  – Company Y — Role → linkedin.com/jobs/...
  ```
- Keep VERIFY as a count only (no list) — they need more research, not immediate action.
- If zero APPLY and zero QUICK-APPLY: still send the message — "всё просмотрено, ничего релевантного в этот раз".

### Error message format

If the pipeline failed at any step and cannot complete — send a short error notification to the **same group**:

```
mcp__telegram-helper__send_message(
  chat_id=-4783500394,
  parse_mode="html",
  message="""<b>⚠️ Pipeline error</b> · 2026-06-07

Упало на шаге: <step name>
Причина: <one-line error summary>

Что сделано до ошибки: <brief — e.g. "fetch OK, 12 msgs, triage не запустился">
Что нужно: <action — e.g. "обнови TELEGRAM_SESSION_STRING в .env">"""
)
```

Send the error notification **even if only partial work was done**. Better to have an incomplete notification than silence when something breaks.

---

## Sanity notes

- `telegram_state.json` is not in git — per-machine cursor. If lost, next run fetches 30 days back per channel (slow but correct).
- To reset a single channel's cursor for filter re-validation:
  ```bash
  jq 'del(.["<channel>"])' tracking/telegram_state.json > /tmp/s.json && mv /tmp/s.json tracking/telegram_state.json
  ```
- `telegram_inbox.json` is overwritten every run — it's the current snapshot only.
- The pipeline reads the live "Jobs" folder — new channels Oleg added in Telegram are automatically included in the next run.
