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.
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.
``` 1. slswitchuser(name: "matthias") 2. listgmaillabels(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. slgetpreferences() 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
``` Inbox → 01 - Investigating (Label14) → [triage/analyze] → 05 - Archived (Label18) ``` 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.
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.
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
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
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 in:inbox from:microsoft.com subject:career in:inbox from:cargill subject:job
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
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
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.
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
EVERY job in the triage file MUST have a clickable link AND a verified JD. Never list a job based solely on an email digest title. The actual job page must be visited (via Playwright for LinkedIn) and the JD confirmed to exist before adding to the triage. Email digests (especially Sunday JYMBII weekly digests) often contain stale, closed, or non-existent listings. Subagents have been caught hallucinating job IDs from digest emails. Trust but verify - scrape the JD, then list.
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)
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.
LinkedIn requires logged-in session - Playwright only, no scribe/webfetch.
``` browsernavigate(url: "https://www.linkedin.com/jobs/view/{JOBID}") browser_evaluate(function: () => document.querySelector('main').innerText.substring(0, 5000)) ```
Duplicate check: Before scraping, search SeriousLetter for the company: ``` slsearchjobs(q: "{company_name}") ``` Skip if already tracked and not in "pending" state.
Score each job 1-5 against Matthias's profile:
| 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 |
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.
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, copy the previous triage as base, CLEAN IT, then add new finds on top. 3. Clean on carry-forward: Remove entries that are expired/dead links, skipped by user, already applied (move to Applied section), already rejected, or confirmed duplicates. Only carry forward jobs that are still open and actionable. This prevents the file from growing endlessly with stale entries. 4. ALL links must be preserved on entries that ARE carried forward. Never strip links when copying tables.
```bash # Find the most recent triage file find "Notes/2026/" -name "Job Triage*" -type f | sort | tail -1 ```
```markdown # Job Reviews YYYY-MM-DD
[Brief intro - emails processed, sources]
| # | Company | Title | Location | Score | Status | Notes | |---|---------|-------|----------|-------|--------|-------| | 1 | [[01 - Company - Title|Company]] | Title | Location | X/5 | New | Key notes | ...
[Table of jobs scored < 3/5 with brief reason]
[Table of jobs from old triage still needing action]
[Status summary of active pipeline]
[Counts: emails processed, jobs scraped, new jobs found, priorities for next session] ```
```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
[Company, title, key facts]
[Bullet points from JD]
[Key requirements]
Fit factors: [Why this fits Matthias] Concerns: [Gaps or issues] Next step: [Concrete action] ```
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).
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: ``` modifygmailmessage_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
Send ntfy notification: "Job triage complete - N new jobs found, M emails archived"
``` # 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
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: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
in:inbox from:microsoft.com subject:career in:inbox from:cargill subject:job
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"
label:Jobs/01---Investigating
in:inbox (subject:rejection OR subject:Absage OR subject:"Votre candidature" OR subject:unfortunately) ```
from:mail.michaelpage.ch explicitly.label:Jobs/01---Investigating (dashes for spaces/special chars)