# NewTriage Workflow **Trigger phrases:** "go after new jobs", "new job triage", "check for new jobs", "job sweep", "full triage cycle", "sweep inbox for jobs" **Purpose:** Full end-to-end job triage cycle - archive old emails, find new job alerts, scrape JDs, evaluate, produce structured review notes with folder structure. --- ## Overview This workflow runs a complete job triage cycle in 6 phases: 1. Setup and label discovery 2. Email cleanup (archive old investigation emails, label new ones) 3. Extract job URLs from emails and scrape JDs via Playwright 4. Evaluate each job against profile 5. Build structured output (folder note + individual job files) 6. Archive emails and update status **Runtime:** Typically 20-45 minutes depending on number of new jobs found. --- ## Phase 1: Setup ``` 1. sl_switch_user(name: "matthias") 2. list_gmail_labels(mnott@mnott.de) - find label IDs: - Jobs/01 - Investigating = Label_14 - Jobs/05 - Archived = Label_18 3. Find existing triage files in Notes/2026/MM/ 4. sl_get_preferences() for evaluation criteria ``` **Gmail label IDs (as of 2026-03-21):** - `Label_14` = Jobs/01 - Investigating - `Label_15` = Jobs/02 - Applied - `Label_16` = Jobs/03 - Interview - `Label_17` = Jobs/04 - Rejected - `Label_18` = Jobs/05 - Archived --- ## Phase 2: Email Discovery and Label Management ### CRITICAL: Email Label Workflow ``` Inbox → 01 - Investigating (Label_14) → [triage/analyze] → 05 - Archived (Label_18) ``` **NEVER skip 01-Investigating.** Every job alert email goes to Investigating first, regardless of age. Only move to 05-Archived AFTER the email has been fully triaged (all jobs extracted, evaluated, and recorded in the triage file). Putting unanalyzed emails directly into Archived is WRONG. ### DO NOT archive old investigation emails automatically Previous versions of this workflow archived everything in 01-Investigating. That's wrong - old emails may not have been triaged yet. Leave them in Investigating until this sweep processes them. ### Find new job emails in inbox Run ALL of these searches in parallel to catch every source: ``` # Core job platforms in:inbox from:linkedin.com in:inbox from:glassdoor in:inbox from:jobup in:inbox from:experteer in:inbox from:xing in:inbox from:jobscout24 in:inbox from:indeed # Recruiter platforms and staffing agencies in:inbox from:mail.michaelpage.ch in:inbox from:sword OR from:tellentalerts.com in:inbox from:hays in:inbox from:robertwalters in:inbox from:randstad in:inbox from:adecco in:inbox from:manpower # ATS platforms (application confirmations, status updates) in:inbox from:greenhouse.io in:inbox from:lever.co in:inbox from:workday in:inbox from:successfactors in:inbox from:smartrecruiters in:inbox from:teamtailor in:inbox from:personio in:inbox from:avature # Company career sites in:inbox from:jobs2web in:inbox from:microsoft.com subject:career in:inbox from:cargill subject:job # Catch-all subject searches in:inbox subject:"job alert" in:inbox subject:"nouvelles offres" in:inbox subject:"new jobs" in:inbox subject:"neue Stellen" in:inbox subject:"Stellenangebote" in:inbox subject:"Suggestions d'offres" ``` For each relevant email found: - ADD Label_14 (01 - Investigating) - REMOVE INBOX label - Do NOT add Label_18 (05 - Archived) - that happens AFTER triage ### Email triage rules - what to label Label as Investigating: - LinkedIn Job Alerts (jobalerts-noreply@linkedin.com, jobs-listings@linkedin.com) - Glassdoor job digests - JobUp alerts ("nouvelles offres", "Suggestions d'offres") - Experteer alerts ("Karrieremoglichkeiten") - Michael Page alerts (from: mail.michaelpage.ch) - Sword Services alerts (from: tellentalerts.com) - Google Job Alerts - XING job recommendations - JobScout24 recommendations - Company career site notifications (jobs2web, avature, etc.) - Direct company emails about specific job openings Leave in inbox (DO NOT label): - Rejection emails (containing "unfortunately", "regret", "Absage", "leider", "rejection", "Votre candidature") - Interview invitations (handle separately) - Personal correspondence from recruiters Skip/ignore (but still move to Investigating for completeness): - LinkedIn newsletters ("tips for job seekers") - XING birthday notifications - Generic career advice emails --- ## Phase 3: Extract Jobs from ALL Email Sources ### CRITICAL: Read full email bodies - ALWAYS Every job alert email contains job data in the body text. NEVER rely on subject lines alone. Use `get_gmail_message_content(message_id)` for each email to extract ALL jobs. ### Source-specific extraction patterns **LinkedIn Job Alerts:** - Parse full body for URLs: `linkedin.com/jobs/view/XXXXXXXXXX` - Extract ALL job IDs (typically 5-15 per email) - Each email contains: job title, company, location, and LI job ID **JobUp.ch Alerts ("nouvelles offres", "Suggestions d'offres"):** - Job titles, companies, locations, and full JobUp URLs are IN THE EMAIL BODY - URLs follow pattern: `jobup.ch/fr/emplois/detail/{UUID}/` - DO NOT say "need Playwright to fetch" - the data is right there in the email - Extract: title, company, location, and full URL for each listing **Michael Page (from: mail.michaelpage.ch):** - Job titles and links are in the email body - Extract all listed positions with URLs **Experteer:** - Job titles may be in the email or may require login to see details - Extract what's available from the email body first **Glassdoor:** - Daily digests contain company names and locations - Usually junior roles - scan quickly, most will be below threshold **Sword Services (from: tellentalerts.com):** - Job titles and links are in the email body **XING:** - Job recommendations with company names - Skip birthday/social notifications **ATS platforms (Greenhouse, Lever, Workday, etc.):** - These are typically application status updates, not job alerts - Route to UpdateStatus workflow if they're rejection/interview notifications ### Build master job list with REAL LINKS **EVERY job in the triage file MUST have a clickable link.** Never use placeholder text like "LinkedIn (from Python alert)" or "JobUp UUIDs (need Playwright)". If the link was in the email, extract it. If it wasn't, note what's needed to find it. Format: - LinkedIn: `[LI XXXXXXXXXX](https://www.linkedin.com/jobs/view/XXXXXXXXXX)` - JobUp: `[JobUp](https://www.jobup.ch/fr/emplois/detail/UUID/)` - Experteer: `[Experteer](https://www.experteer.ch/career/view-jobs/SLUG)` - Michael Page: `[MP](URL from email)` - Company sites: `[Company](URL from email)` ### Pre-triage by title/level **CRITICAL: NEVER auto-exclude jobs. Include EVERYTHING in the triage list. Rank low if needed, but the user decides what to skip.** The user wants to see all jobs and make their own decisions. Your job is to rank, not to filter. "Head of Product Management" IS leadership. "Senior PM" IS a valid target. Do not presume to know what the user wants to skip. **Rank 4-5/5 (strong):** - CIO, CTO, Director, Head of (ANY), VP, Lead, Principal, Architect (senior) - IC roles at senior+ level (Principal Engineer, Staff Architect, Lead Data Scientist, Field CTO) - Consulting, advisory roles at senior/manager+ level **Rank 2-3/5 (include but lower):** - Pure coding IC roles (frontend, backend, full-stack developer) - Product management, project management (these ARE valid - never skip) - Academic positions (postdoc, PhD) - Domain stretch (different industry but transferable skills) **Rank 1/5 (include at bottom):** - Junior, apprentice, intern, stagiaire, entry-level - Wrong country without remote option - Salary explicitly below CHF 150k **The ONLY thing you can omit entirely:** Duplicate listings already in the triage file. ### Scrape full JDs via Playwright (for promising candidates only) **LinkedIn requires logged-in session - Playwright only, no scribe/webfetch.** ``` browser_navigate(url: "https://www.linkedin.com/jobs/view/{JOB_ID}") browser_evaluate(function: () => document.querySelector('main').innerText.substring(0, 5000)) ``` **Duplicate check:** Before scraping, search SeriousLetter for the company: ``` sl_search_jobs(q: "{company_name}") ``` Skip if already tracked and not in "pending" state. --- ## Phase 4: Evaluate Score each job 1-5 against Matthias's profile: ### Profile Summary (for scoring) - **Target roles:** CIO, CTO, VP Engineering, Director IT, Head of IT, Head of Data, Principal/Lead (senior) - **Location preference:** Suisse romande (Geneva, Vaud, Valais) - highest; Zurich hybrid - acceptable; Bern/Basel - lower; fully remote - acceptable - **Salary floor:** CHF 150k minimum - **Languages:** Native DE/EN/FR - huge advantage for trilingual roles - **Domain strengths:** SAP, data/analytics, architecture, governance, transformation, financial services, consulting - **Experience type:** Both corporate IT leadership AND consulting engagements fit ### Scoring Rubric | Score | Meaning | Criteria | |-------|---------|---------| | 5/5 | Pursue immediately | Perfect title + location + domain alignment | | 4/5 | Strong pursue | Good title + location, minor gaps | | 3/5 | Consider | Title or location mismatch, otherwise good fit | | 2/5 | Low priority | Multiple mismatches, some relevance | | 1/5 | Skip | Wrong domain, wrong level, or irrelevant | ### Threshold **Only include in triage file if score >= 3/5.** Jobs scoring 2/5 or below are noted briefly in the "below threshold" table and ignored. --- ## Phase 5: Build Output ### Triage File Location and Carry-Forward Logic **CRITICAL: Date-based triage files with carry-forward.** ``` Notes/2026/MM/YYYY-MM-DD/Job Triage YYYY-MM-DD/ Job Triage YYYY-MM-DD.md ``` **Rules:** 1. **Same date as previous sweep:** Update the existing triage file in-place. Add new finds, update statuses. 2. **New date:** Create a new date folder, create a new triage file, and CARRY FORWARD the entire previous triage file as the base. Then add new finds on top. 3. **Carry-forward means:** Copy the full master list (all tiers, all links, all statuses) from the previous triage into the new file. The new file is the single source of truth. 4. **ALL links must be preserved.** When carrying forward, keep every column including Type and Link. Never strip links when copying tables. ### Finding the previous triage file ```bash # Find the most recent triage file find "Notes/2026/" -name "Job Triage*" -type f | sort | tail -1 ``` ### File structure ### Main file (folder note) structure ```markdown # Job Reviews YYYY-MM-DD [Brief intro - emails processed, sources] ## New Jobs (YYYY-MM-DD batch) | # | Company | Title | Location | Score | Status | Notes | |---|---------|-------|----------|-------|--------|-------| | 1 | [[01 - Company - Title|Company]] | Title | Location | X/5 | New | Key notes | ... ### Evaluated but below threshold [Table of jobs scored < 3/5 with brief reason] ## Pending from Previous Triage [Table of jobs from old triage still needing action] ## Already Applied / Closed / Not Pursuing [Status summary of active pipeline] ## Review Notes [Counts: emails processed, jobs scraped, new jobs found, priorities for next session] ``` ### Individual job file structure ```markdown # NN - Company - Title **Date found:** YYYY-MM-DD **Source:** [where found] **URL:** [LinkedIn/company URL] **Location:** [city, country] ([work arrangement]) **Type:** [Full-time/contract], [permanent/fixed-term] **Score:** X/5 **Status:** New / Pending / Applied --- ## Job Description [Company, title, key facts] ### Key Responsibilities [Bullet points from JD] ### Requirements [Key requirements] --- ## Evaluation **Fit factors:** [Why this fits Matthias] **Concerns:** [Gaps or issues] **Next step:** [Concrete action] ``` ### Note numbering Individual job files within the review folder use sequential 2-digit numbers: 01, 02, 03... These are NOT the global 4-digit note numbers (those are used for applied job folders 0XXX). --- ## Phase 6: Email Archiving (Post-Triage) **ONLY NOW move emails from 01-Investigating to 05-Archived.** This is the final step, after all jobs have been extracted and recorded. For each email that was fully processed in this sweep: ``` modify_gmail_message_labels: add: Label_18 (05 - Archived) remove: Label_14 (01 - Investigating) ``` **Do NOT archive:** - Rejection emails (leave in inbox for separate handling) - Emails that weren't fully processed in this sweep - Interview invitations ### Completion notification Send ntfy notification: "Job triage complete - N new jobs found, M emails archived" --- ## Gmail Search Queries Reference ``` # Core job platforms in:inbox from:linkedin.com in:inbox from:glassdoor in:inbox from:jobup in:inbox from:experteer in:inbox from:xing in:inbox from:jobscout24 in:inbox from:indeed # Recruiter agencies (CRITICAL - easily missed) in:inbox from:mail.michaelpage.ch in:inbox from:sword OR from:tellentalerts.com in:inbox from:hays in:inbox from:robertwalters in:inbox from:randstad # ATS and career platforms in:inbox from:greenhouse.io in:inbox from:lever.co in:inbox from:workday in:inbox from:successfactors in:inbox from:smartrecruiters in:inbox from:teamtailor in:inbox from:personio in:inbox from:avature in:inbox from:jobs2web # Company-specific in:inbox from:microsoft.com subject:career in:inbox from:cargill subject:job # Catch-all (run last to pick up stragglers) in:inbox subject:"job alert" in:inbox subject:"nouvelles offres" in:inbox subject:"Suggestions d'offres" in:inbox subject:"new jobs" in:inbox subject:"neue Stellen" in:inbox subject:"Stellenangebote" # Currently in investigation label label:Jobs/01---Investigating # Rejections (leave in inbox) in:inbox (subject:rejection OR subject:Absage OR subject:"Votre candidature" OR subject:unfortunately) ``` --- ## Notes and Lessons Learned - **JobUp emails contain full job data:** Titles, companies, locations, AND URLs are all in the email body. NEVER say "need Playwright to fetch" for JobUp. - **Michael Page domain is mail.michaelpage.ch:** Standard searches for "michaelpage" miss it. Always search `from:mail.michaelpage.ch` explicitly. - **NEVER skip 01-Investigating:** The workflow is inbox → Investigating → Archived. Putting emails directly into Archived skips the triage step. Even old emails may contain untriaged jobs. - **ALWAYS preserve links in triage files:** When carrying forward or consolidating, every job row must have a clickable link. Never use placeholder text. - **Read full email bodies for ALL sources:** LinkedIn, JobUp, Michael Page, Experteer, Sword - all contain job data in the body. Subject/metadata is never enough. - **Playwright only for LinkedIn JD scraping:** scribe, webfetch all fail or truncate LinkedIn; always use Playwright with logged-in session - **Sequential Playwright:** Playwright scraping is sequential (one browser session); scrape only the top candidates (8-12 max per triage cycle) - **UN jobs:** IOM and other UN agencies typically prefer internal/female/non-represented-country candidates as first tier - factor into go/no-go decision - **Label_14 query:** The Gmail search syntax is `label:Jobs/01---Investigating` (dashes for spaces/special chars)