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

7.1 KiB

name description
triage-jobs 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:

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:

~/.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:

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.