cv-2026/.claude/skills/triage-jobs/SKILL.md

169 lines
7.1 KiB
Markdown

---
name: triage-jobs
description: Triage the latest Telegram vacancy inbox (`tracking/telegram_inbox.json`) — stratifies by priority (p1 in this session, p2/p3 via a Haiku subagent) and returns a deduped shortlist of vacancies worth applying to. Use when the user says "разбери inbox", "что нового по работе", "разбери вакансии", "пройдись по telegram-вакансиям", "triage the inbox", "find relevant jobs from telegram", or similar.
---
# triage-jobs
Read the latest Telegram vacancy fetch and present a shortlist that fits Oleg's targeting.
The inbox file is large (~200K tokens). To keep the main session lean, p2/p3 channels go to a **Haiku subagent** that returns only finalists. p1 (small, high-signal) is processed here.
## Args
Optional priority filter as positional arg(s): `p1`, `p2`, `p3`, or `all` (default).
- `/triage-jobs` → all three tiers
- `/triage-jobs p1` → only p1 in this session, skip subagent
- `/triage-jobs p2 p3` → only subagent run
## Step 1 — Pre-checks
Verify the inbox exists and is recent:
```bash
ls -lh tracking/telegram_inbox.json
jq '{generated_at, total_in_inbox, channels_count: (.channels | length)}' tracking/telegram_inbox.json
```
If the file is missing or its `generated_at` is older than ~6 hours, **don't run triage on stale data** — offer to refetch first:
```bash
~/.local/bin/uv run scripts/list_telegram_channels.py \
| ~/.local/bin/uv run scripts/fetch_telegram_jobs.py -
```
## Step 2 — Oleg's targeting (apply strictly during triage)
This is the rubric — use it verbatim when deciding "keep or drop" and when briefing the subagent.
**Roles he targets:**
- Senior / Staff / Principal Full-Stack Engineer
- Tech Lead, Engineering Lead, Engineering Manager (with hands-on)
- AI Engineer / Applied AI / LLM Engineer (TS or Python OK for AI roles)
**Stack match (strong signal):** TypeScript, JavaScript, Node.js, React, Next.js, TanStack, Tailwind, PostgreSQL, Drizzle, Vercel, Cloudflare, Sanity/Storyblok/Contentful/Payload (Headless CMS), Shopify/Hydrogen, GraphQL, WebSockets. For AI roles also: LLM orchestration, MCP, RAG, embeddings, Mastra, Vercel AI SDK, Claude/GPT/Gemini APIs.
**Culture must-haves:**
- **Global remote** (he's in GMT+7, full EMEA overlap + US East AM). EMEA / global / US-East-friendly TZ all fine. "Remote within Russia only" or "US only — must be in EST 9-5" → reject.
- **Compensation in USD/EUR** preferred. Target ~$100k+ FT or $70+/hr contractor. Russian-RUB roles at ₽70-100k/mo (≈ $750-1100) are below floor.
- Deel/W-8BEN contractor format is a plus.
**Deal-breakers (auto-reject):**
- Mobile-native (Kotlin, Swift, Android, iOS, Flutter)
- Non-stack backend (Go/Golang, Java, .NET, C#, Ruby, PHP, Rust, Scala) **as primary** — if the role is fullstack with React/Node + Go on side, that's fine
- DevOps / SRE as primary role
- QA / Manual testing
- Sales, Marketing, Designer, Recruiter, PM (non-engineering)
- Junior / Trainee / Intern
- On-site outside major remote-friendly hubs (e.g. Lagos, low-cost-region on-site)
- Sub-$50k FT compensation when the salary is stated
**Stretch interests (consider even if not perfect match):**
- AI/ML engineering roles using Python (his AI CV covers this)
- Vetted-contractor platforms (Toptal, Lemon.io, Turing) — separate financial track
- Headless CMS, Shopify Hydrogen, eCommerce platforms
- Roles at companies building dev tooling, AI agents, MCP ecosystem (his open-source overlaps)
For canonical source-of-truth, the CVs are at:
- `base/oleg_proskurin_ai_engineer_fullstack_cv.md`
- `base/oleg_proskurin_fullstack_techlead_cv.md`
## Step 3 — p1 (process here)
Pull p1 channels from inbox and walk through every kept message:
```bash
jq '.channels | to_entries | map(select(.value.priority == "p1")) | from_entries' tracking/telegram_inbox.json
```
For each kept message, classify:
- **Apply** — fits role + culture + comp. Note: company, role, link, why-fit (1 line).
- **Maybe** — fits role/stack but unclear comp or stretch culture. Note same fields + the uncertainty.
- **Drop** — fails targeting. Don't list, don't explain.
p1 should be small enough (~12K tokens currently) to do in main session without context strain.
## Step 4 — p2 and p3 (delegate to Haiku subagent)
Spawn a subagent via the Agent tool. Use `general-purpose` agent type with **Haiku model** for cost/speed.
**Critical:** the subagent does not see this conversation. The prompt must be self-contained.
Template (fill `<PRIORITY>` with `p2`, or pass both p2 and p3 in one call):
```
Agent({
description: "Triage Telegram inbox <PRIORITY>",
subagent_type: "general-purpose",
model: "haiku",
prompt: `
Triage job postings from Oleg's Telegram inbox.
Read tracking/telegram_inbox.json and filter to channels with priority "<PRIORITY>":
jq '.channels | to_entries | map(select(.value.priority == "<PRIORITY>")) | from_entries' tracking/telegram_inbox.json
Oleg's targeting (apply strictly):
[paste the "Step 2 — Oleg's targeting" section verbatim]
For each kept message that is a REAL vacancy (not a resume/CV digest entry, not a chat-room message, not a market-intel essay), decide if it fits the targeting.
Return ONLY the shortlist as JSON. Reject everything else silently — no commentary on rejected items.
Shortlist schema:
[
{
"channel": "<channel_key>",
"id": <message_id>,
"link": "<t.me url>",
"title": "<role title>",
"company": "<company or null>",
"stack": ["<key tech tokens>"],
"comp": "<salary string or null>",
"remote": true | false | "unclear",
"fit": "apply" | "maybe",
"why": "<one short sentence>"
}
]
If there are zero matches, return [].
Do not paraphrase or summarize messages — quote the original title verbatim and just extract structured fields.
Do not include personal opinions or formatting commentary.
`
})
```
Run subagents in parallel where possible (one for p2, one for p3 in the same message).
## Step 5 — Aggregate and present
Combine p1 finalists (from Step 3) with subagent shortlists (Step 4). Dedupe by `(company, title)` pair when possible.
Present grouped output to Oleg, e.g.:
```
🎯 APPLY (N)
- jaabz_com #10233 — AI-Native Full Stack Developer @ Geeky Tech — TS/React/Python, Fully Remote, B2B SaaS
- dev_connectablejobs #2039 — Full-Stack Engineer @ VOYGR — AI-native, $4-7k, Remote, founders ex-Google
- ...
🤔 MAYBE (N)
- jsspeak #58062 — AI Engineer (Python & Node.js) Senior @ Eshe App — 300-400k₽, RU+BY citizenship only — fit but comp lower
- ...
```
## Step 6 — Suggest next step
After the shortlist, offer to:
- Append apply-list to `tracking/applications.md` (one row each, status `to-apply`).
- For 1-2 top picks, switch to the tailoring workflow (see main CLAUDE.md "Workflow 2 — Tailor CV").
## Notes
- **Don't auto-add to `applications.md`** without explicit confirmation — Oleg curates that file.
- **Don't auto-refetch.** If the inbox is stale, ask first.
- **State cursor advances on every fetch.** A skill run only reads the existing inbox — it doesn't trigger a new fetch unless explicitly requested.
- **Skip P3 by default if user says "quick triage"** — p3 is mostly market-intel and dead channels, low ROI.