chore: move cv and job applications to private rafapolo/job, add delivery-lead watcher
cv/ and jobs/ become symlinks into ../rafapolo/job (gitignored there). scripts/watch-delivery-lead.ts runs daily via launchd and alerts on Telegram when the MRP-Global agentic Delivery Lead role resurfaces or closes.
This commit is contained in:
+3
-5
@@ -4,9 +4,7 @@ node_modules/
|
||||
|
||||
# job data and personal notes
|
||||
results/
|
||||
jobs/
|
||||
# symlinks into the private ../rafapolo/job/ (gitignored there too)
|
||||
jobs
|
||||
cv
|
||||
|
||||
# CV files (large binaries + personal)
|
||||
cv/*.pdf
|
||||
cv/*.odt
|
||||
cv/*.html
|
||||
|
||||
@@ -30,10 +30,10 @@ telegram.ts # Telegram delivery, chunking, --setup chat-ID discovery
|
||||
scripts/ # one-off search scripts; import from ../config.ts and ../fetch.ts
|
||||
scripts/scicore-watch.sh # daily launchd watch on sciCORE's open-positions page
|
||||
results/sent.txt # links already pushed to Telegram — never sent twice
|
||||
cv/ → ../rafapolo/job/cv/ # symlink; CVs (cv-source.md is canonical, polo.md is the ranking profile)
|
||||
cv/polo.md # Rafael's profile — use for ranking jobs
|
||||
cv/ # CV files (html, pdf, odt)
|
||||
results/jobs.csv # output — one row per job
|
||||
jobs/ # notes on specific job leads
|
||||
jobs/ → ../rafapolo/job/applications/ # symlink; one folder per company: cover/motivation letters, dossiers, notes
|
||||
plans/ # LinkedIn post drafts
|
||||
```
|
||||
|
||||
@@ -178,6 +178,12 @@ Extra filters for temp scripts (import from `../fetch.ts`):
|
||||
- `isEnglishContent(id)` — drop DE/FR description content
|
||||
- Add your own inline RE for EMEA, .NET, etc.
|
||||
|
||||
## Application history (`jobs/`, `cv/`)
|
||||
|
||||
`cv/` and `jobs/` are symlinks into the **private** `../rafapolo/job/` (gitignored in both repos; `rafapolo` is a public GitHub Pages site, so never move these into a non-ignored path there). Everything else about past applications lives beside them: `../rafapolo/job/career-strategy-notes.md`, `credentials/` (certs, diplomas, references) and the dossier PDFs at the root.
|
||||
|
||||
Before presenting a job or writing a letter, check `jobs/<company-slug>/` (e.g. `ls jobs | grep -i roche`): if a folder exists, Rafael already applied there — say so, reuse the earlier letter as a starting point, and mention any track record in memory (e.g. Adobe and BIS cold applies are dead). New letters go in `jobs/<company-slug>/`.
|
||||
|
||||
## Ranking jobs against the CV
|
||||
|
||||
Read `cv/polo.md` for the full profile. Key signals:
|
||||
@@ -216,6 +222,7 @@ Runs as a **launchd agent** — this machine has no cron at all, so don't reach
|
||||
| plist (`~/Library/LaunchAgents/`) | when | runs |
|
||||
|---|---|---|
|
||||
| `com.rafapolo.linkme-daily` | 09:00 / 12:00 / 15:00 / 17:00 / 20:00 | `bun run daily.ts` → `results/daily.log` |
|
||||
| `com.rafapolo.linkme-watch-delivery-lead` | 10:00 | `bun run scripts/watch-delivery-lead.ts` → `results/watch-delivery-lead.log`; watches for the MRP-Global "Delivery Lead – Agentic Solutions" opening (LinkedIn 4473851998) resurfacing elsewhere and for the original closing. Telegram only on a hit. Unload once the lead is resolved |
|
||||
|
||||
If the Mac is asleep at the scheduled time, launchd fires the job on wake rather than skipping the day. `launchctl list | grep linkme` to check it's loaded; `launchctl unload <plist>` to stop it.
|
||||
|
||||
|
||||
@@ -1,234 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Rafael Polo - CV</title>
|
||||
<style>
|
||||
@page {
|
||||
size: 8.2681in 11.6929in;
|
||||
margin: 0.5in;
|
||||
}
|
||||
body {
|
||||
font-family: Calibri, Liberation Sans, sans-serif;
|
||||
font-size: 11pt;
|
||||
line-height: 1.2;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.container {
|
||||
display: table;
|
||||
width: 100%;
|
||||
}
|
||||
.left-col {
|
||||
display: table-cell;
|
||||
width: 100%;
|
||||
}
|
||||
.title {
|
||||
font-size: 24pt;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.05in;
|
||||
}
|
||||
.subtitle {
|
||||
font-size: 11pt;
|
||||
font-style: italic;
|
||||
margin-bottom: 0.15in;
|
||||
color: #444;
|
||||
}
|
||||
table.personal {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 0.2in;
|
||||
}
|
||||
table.personal td {
|
||||
padding: 0.02in 0.05in;
|
||||
vertical-align: top;
|
||||
}
|
||||
table.personal .label {
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
width: 1in;
|
||||
}
|
||||
table.personal .header-row td {
|
||||
font-weight: bold;
|
||||
font-size: 11pt;
|
||||
text-transform: uppercase;
|
||||
border-bottom: 1px solid #999;
|
||||
padding-bottom: 0.05in;
|
||||
}
|
||||
h3 {
|
||||
font-size: 11pt;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
margin: 0.15in 0 0.08in 0;
|
||||
border-bottom: 1px solid #999;
|
||||
padding-bottom: 0.03in;
|
||||
}
|
||||
ul {
|
||||
margin: 0.05in 0;
|
||||
padding-left: 0.15in;
|
||||
}
|
||||
li {
|
||||
margin-bottom: 0.04in;
|
||||
font-size: 10pt;
|
||||
}
|
||||
.job-item {
|
||||
margin-bottom: 0.08in;
|
||||
}
|
||||
.job-title {
|
||||
font-weight: bold;
|
||||
font-size: 10pt;
|
||||
}
|
||||
.job-meta {
|
||||
font-style: italic;
|
||||
font-size: 9pt;
|
||||
color: #555;
|
||||
}
|
||||
.job-desc {
|
||||
font-style: italic;
|
||||
font-size: 10pt;
|
||||
color: #444;
|
||||
margin-left: 0.1in;
|
||||
}
|
||||
.edu-item {
|
||||
margin-bottom: 0.05in;
|
||||
}
|
||||
.edu-degree {
|
||||
font-weight: bold;
|
||||
font-size: 10pt;
|
||||
}
|
||||
.edu-school {
|
||||
font-style: italic;
|
||||
font-size: 10pt;
|
||||
}
|
||||
.skill-item {
|
||||
font-size: 10pt;
|
||||
margin-bottom: 0.02in;
|
||||
}
|
||||
.lang-item {
|
||||
font-size: 10pt;
|
||||
margin-bottom: 0.02in;
|
||||
}
|
||||
.footer {
|
||||
font-size: 9pt;
|
||||
color: #666;
|
||||
margin-top: 0.2in;
|
||||
padding-top: 0.1in;
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="title">Rafael Polo</div>
|
||||
<div class="subtitle">Senior Software Engineer — Platform, Data & AI</div>
|
||||
|
||||
<table class="personal">
|
||||
<tr>
|
||||
<td colspan="2" class="header-row">Personal</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">First name:</td>
|
||||
<td>Rafael</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Last name:</td>
|
||||
<td>Polo</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Citizenship:</td>
|
||||
<td>Portuguese (EU) · Brazilian — no work permit required</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Address:</td>
|
||||
<td>Basel, 4055, Switzerland</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Email:</td>
|
||||
<td>rafael@extrapolo.com</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Web:</td>
|
||||
<td>linkedin.com/in/rafaelpolo<br>extrapolo.com</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h3>Formal Education</h3>
|
||||
<ul>
|
||||
<li class="edu-item">
|
||||
<span class="edu-degree"><em><strong>Postgraduate in Data Science for Finance</strong></em> – 2021</span><br>
|
||||
<span class="edu-school">NOVA IMS Information Management School, Lisbon</span>
|
||||
</li>
|
||||
<li class="edu-item">
|
||||
<span class="edu-degree"><em><strong>Master in Information Systems</strong></em> – 2010–2012</span><br>
|
||||
<span class="edu-school">PPGI/UNIRIO — Knowledge Representation & Reasoning</span>
|
||||
</li>
|
||||
<li class="edu-item">
|
||||
<span class="edu-degree"><em><strong>Bachelor in Computer Science</strong></em> – 2004–2009</span><br>
|
||||
<span class="edu-school">Metodista Bennett, Rio de Janeiro</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Relevant Professional Experience</h3>
|
||||
<ul>
|
||||
<li class="job-item">
|
||||
<span class="job-title">Swisscom</span> – <span class="job-meta">2024–2025 – Zürich, Switzerland</span><br>
|
||||
<span class="job-desc"><strong>DevOps Engineer</strong> developing platforms for Telecommunication Operations.</span>
|
||||
</li>
|
||||
<li class="job-item">
|
||||
<span class="job-title">EDGE Empower®</span> – <span class="job-meta">2024–2025 – Baar, Switzerland</span><br>
|
||||
<span class="job-desc"><strong>Senior Technical Lead</strong> managing projects and external UX & Dev Teams for a SaaS Product.</span>
|
||||
</li>
|
||||
<li class="job-item">
|
||||
<span class="job-title">ExtraPolo — Independent Consulting</span> – <span class="job-meta">2010–2022 – Rio de Janeiro · Berlin · Athens · Lisbon</span><br>
|
||||
<span class="job-desc">40+ projects across civic tech, data journalism, open-source platforms and interactive installations for public and private clients.</span>
|
||||
</li>
|
||||
<li class="job-item">
|
||||
<span class="job-title">Moedax Digital Services</span> – <span class="job-meta">2018–2021 – Lisbon · Rio de Janeiro</span><br>
|
||||
<span class="job-desc"><strong>Founder & CTO</strong> – Fintech startup connecting CBDC digital currency with KYC compliance automation.</span>
|
||||
</li>
|
||||
<li class="job-item">
|
||||
<span class="job-title">GFOSS.eu / Code + Create</span> – <span class="job-meta">2017–2018 – Athens, Greece</span><br>
|
||||
<span class="job-desc"><strong>Web Programming Teacher</strong> – taught HTML, CSS, jQuery to refugee and local youth.</span>
|
||||
</li>
|
||||
<li class="job-item">
|
||||
<span class="job-title">Tactical Tech Collective</span> – <span class="job-meta">2014–2016 – Berlin, Germany</span><br>
|
||||
<span class="job-desc"><strong>Full-Stack Web Architect</strong> – investigative data tools under Exposing the Invisible team.</span>
|
||||
</li>
|
||||
<li class="job-item">
|
||||
<span class="job-title">Trommsdorf + Drüner</span> – <span class="job-meta">2013–2014 – Berlin, Germany</span><br>
|
||||
<span class="job-desc"><strong>Full-Stack Engineer</strong> – Innovation Team developing Business Intelligence dashboards.</span>
|
||||
</li>
|
||||
<li class="job-item">
|
||||
<span class="job-title">Cortex Intelligence</span> – <span class="job-meta">2007–2008 – Rio de Janeiro, Brazil</span><br>
|
||||
<span class="job-desc"><strong>Developer</strong> – Competitive Intelligence and Data Mining R&D team.</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Certifications</h3>
|
||||
<ul>
|
||||
<li class="skill-item">Swiss Federal Act on Data Protection (nFADP)</li>
|
||||
<li class="skill-item">Gen AI Foundational Models — NLP & LLMs</li>
|
||||
<li class="skill-item">AI Agents using RAG and LangChain</li>
|
||||
</ul>
|
||||
|
||||
<h3>Relevant Tech Skills</h3>
|
||||
<ul>
|
||||
<li class="skill-item"><strong>Languages & Frameworks:</strong> Python · Ruby on Rails · TypeScript · React · Java</li>
|
||||
<li class="skill-item"><strong>DevOps & Infra:</strong> Docker · Kubernetes · CI/CD · AWS · Linux · Git</li>
|
||||
<li class="skill-item"><strong>Data & AI:</strong> SQL · ETL · Data Engineering · RAG · LangChain · LLM integrations</li>
|
||||
<li class="skill-item"><strong>Architecture:</strong> REST APIs · MVC · OOP · UML · System Design · Web Crawling</li>
|
||||
</ul>
|
||||
|
||||
<h3>Natural Languages</h3>
|
||||
<ul>
|
||||
<li class="lang-item">Native Brazilian Portuguese</li>
|
||||
<li class="lang-item">Full professional English</li>
|
||||
<li class="lang-item">Full professional Spanish</li>
|
||||
<li class="lang-item">German B1 – TELC certified · 3 yrs Berlin + 3 yrs Basel</li>
|
||||
</ul>
|
||||
|
||||
<div class="footer">
|
||||
References available upon request · CV updated May 2026
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,196 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Rafael Polo - CV</title>
|
||||
<style>
|
||||
@page {
|
||||
size: 8.2681in 11.6929in;
|
||||
margin: 0.5in;
|
||||
}
|
||||
body {
|
||||
font-family: Liberation Sans, Carlito, Georgia, serif;
|
||||
font-size: 11pt;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.title {
|
||||
font-size: 36pt;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.1in;
|
||||
}
|
||||
.subtitle {
|
||||
font-size: 14pt;
|
||||
margin-bottom: 0.3in;
|
||||
color: #333;
|
||||
}
|
||||
h1 {
|
||||
font-size: 24pt;
|
||||
font-weight: bold;
|
||||
margin: 0.2in 0 0.1in 0;
|
||||
border-bottom: 1px solid #333;
|
||||
padding-bottom: 0.05in;
|
||||
}
|
||||
h2 {
|
||||
font-size: 18pt;
|
||||
font-weight: bold;
|
||||
margin: 0.15in 0 0.08in 0;
|
||||
}
|
||||
h3 {
|
||||
font-size: 14pt;
|
||||
font-weight: bold;
|
||||
margin: 0.1in 0 0.05in 0;
|
||||
}
|
||||
.section {
|
||||
margin-bottom: 0.15in;
|
||||
}
|
||||
.personalinline {
|
||||
font-size: 10pt;
|
||||
margin-bottom: 0.2in;
|
||||
}
|
||||
.personalinline span {
|
||||
margin-right: 0.3in;
|
||||
}
|
||||
.key-value {
|
||||
display: flex;
|
||||
margin: 0.03in 0;
|
||||
}
|
||||
.key {
|
||||
font-weight: bold;
|
||||
width: 1.5in;
|
||||
}
|
||||
.value {
|
||||
flex: 1;
|
||||
}
|
||||
ul {
|
||||
margin: 0.05in 0;
|
||||
padding-left: 0.2in;
|
||||
}
|
||||
li {
|
||||
margin-bottom: 0.03in;
|
||||
}
|
||||
.small {
|
||||
font-size: 10pt;
|
||||
}
|
||||
.experience-item {
|
||||
margin-bottom: 0.1in;
|
||||
}
|
||||
.exp-header {
|
||||
font-weight: bold;
|
||||
}
|
||||
.exp-meta {
|
||||
font-style: italic;
|
||||
font-size: 10pt;
|
||||
}
|
||||
.exp-desc {
|
||||
margin-left: 0.2in;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="title">Rafael Polo</div>
|
||||
<div class="subtitle">Senior Software Engineer — Platform, Data & AI</div>
|
||||
|
||||
<div class="personalinline">
|
||||
<span>Basel, 4055, Switzerland</span>
|
||||
<span>rafael@extrapolo.com</span>
|
||||
<span>linkedin.com/in/rafaelpolo</span>
|
||||
<span>extrapolo.com</span>
|
||||
</div>
|
||||
|
||||
<div class="personalinline">
|
||||
<span>Portuguese (EU) · Brazilian — no work permit required</span>
|
||||
<span>20+ years building production systems across Switzerland, Germany, Greece and Brazil</span>
|
||||
<span>Fluent English · German B1 TELC (3 yrs Berlin + 3 yrs Basel) · EU citizen, immediately available</span>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h1>P R O F E S S I O N A L E X P E R I E N C E</h1>
|
||||
|
||||
<div class="experience-item">
|
||||
<div class="exp-header">Swisscom</div>
|
||||
<div class="exp-meta">DevOps Engineer · Zürich, Switzerland</div>
|
||||
<div class="exp-desc">Modernization of mission-critical internal platforms & CI/CD pipelines for high-availability Telecom infrastructure.</div>
|
||||
</div>
|
||||
|
||||
<div class="experience-item">
|
||||
<div class="exp-header">EDGE Empower®</div>
|
||||
<div class="exp-meta">Senior Technical Lead · Baar, Switzerland · 2024 – 2025</div>
|
||||
<div class="exp-desc">Built a PayTool cutting processing time by 98%; reduced CI/CD pipeline from 14 → 4 minutes; managed 3 developers and a UX designer for a SaaS product.</div>
|
||||
</div>
|
||||
|
||||
<div class="experience-item">
|
||||
<div class="exp-header">ExtraPolo — Independent Consulting</div>
|
||||
<div class="exp-meta">Software Engineer & Consultant · Rio de Janeiro · Berlin · Athens · Lisbon · 2010 – 2022</div>
|
||||
<div class="exp-desc">40+ projects across civic tech, data journalism, open-source platforms and interactive installations for public and private clients.</div>
|
||||
</div>
|
||||
|
||||
<div class="experience-item">
|
||||
<div class="exp-header">Moedax Digital Services</div>
|
||||
<div class="exp-meta">Founder & CTO · Lisbon · Rio de Janeiro · 2018 – 2021</div>
|
||||
<div class="exp-desc">Fintech startup connecting CBDC digital currency with KYC compliance automation; built data pipelines and banking integrations in Python.</div>
|
||||
</div>
|
||||
|
||||
<div class="experience-item">
|
||||
<div class="exp-header">GFOSS.eu / Code + Create</div>
|
||||
<div class="exp-meta">Web Programming Teacher · Athens, Greece · 2017 – 2018</div>
|
||||
<div class="exp-desc">Taught web programming (HTML, CSS, jQuery) to refugee and local youth through the EU-funded Code + Create initiative.</div>
|
||||
</div>
|
||||
|
||||
<div class="experience-item">
|
||||
<div class="exp-header">Tactical Tech Collective</div>
|
||||
<div class="exp-meta">Full-Stack Web Architect · Berlin, Germany · Dec 2014 – Jun 2016</div>
|
||||
<div class="exp-desc">Built investigative data tools and platforms under the Exposing the Invisible team (Ruby on Rails, data visualisation).</div>
|
||||
</div>
|
||||
|
||||
<div class="experience-item">
|
||||
<div class="exp-header">Trommsdorff + Drüner</div>
|
||||
<div class="exp-meta">Full-Stack Engineer · Berlin, Germany</div>
|
||||
<div class="exp-desc">Innovation team — Business Intelligence dashboards for marketing consultancy clients.</div>
|
||||
</div>
|
||||
|
||||
<div class="experience-item">
|
||||
<div class="exp-header">Cortex Intelligence</div>
|
||||
<div class="exp-meta">Developer · Rio de Janeiro, Brazil</div>
|
||||
<div class="exp-desc">Competitive Intelligence and Data Mining R&D team.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h1>E D U C A T I O N</h1>
|
||||
<div class="key-value"><span class="key">Postgraduate in Data Science for Finance</span><span class="value">NOVA IMS Information Management School, Lisbon — 2021</span></div>
|
||||
<div class="key-value"><span class="key">Master in Information Systems</span><span class="value">PPGI/UNIRIO — Knowledge Representation & Reasoning — 2010–2012</span></div>
|
||||
<div class="key-value"><span class="key">Bachelor in Computer Science</span><span class="value">Metodista Bennett, Rio de Janeiro — 2004–2009</span></div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h1>L A N G U A G E S</h1>
|
||||
<div class="key-value"><span class="key">Portuguese</span><span class="value">native</span></div>
|
||||
<div class="key-value"><span class="key">English</span><span class="value">full professional</span></div>
|
||||
<div class="key-value"><span class="key">Spanish</span><span class="value">full professional</span></div>
|
||||
<div class="key-value"><span class="key">German</span><span class="value">B1 — TELC certified · 3 yrs Berlin + 3 yrs Basel</span></div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h1>C E R T I F I C A T I O N S</h1>
|
||||
<ul>
|
||||
<li>Swiss Federal Act on Data Protection (nFADP)</li>
|
||||
<li>Gen AI Foundational Models — NLP & LLMs</li>
|
||||
<li>AI Agents using RAG and LangChain</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h1>T E C H S K I L L S</h1>
|
||||
<div class="key-value"><span class="key">Languages & Frameworks</span><span class="value">Python · Ruby on Rails · TypeScript · React · Java</span></div>
|
||||
<div class="key-value"><span class="key">DevOps & Infra</span><span class="value">Docker · Kubernetes · CI/CD · AWS · Linux · Git</span></div>
|
||||
<div class="key-value"><span class="key">Data & AI</span><span class="value">SQL · ETL · Data Engineering · RAG · LangChain · LLM integrations · Data Science</span></div>
|
||||
<div class="key-value"><span class="key">Architecture</span><span class="value">REST APIs · MVC · OOP · UML · System Design · Web Crawling & Scraping</span></div>
|
||||
</div>
|
||||
|
||||
<div class="small">
|
||||
References available upon request · CV updated May 2026
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,243 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Rafael Polo - CV</title>
|
||||
<style>
|
||||
@page {
|
||||
size: 8.2681in 11.6929in;
|
||||
margin: 0.5in;
|
||||
}
|
||||
body {
|
||||
font-family: Liberation Sans, Carlito, Calibri, Georgia, sans-serif;
|
||||
font-size: 11pt;
|
||||
line-height: 1.3;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.container {
|
||||
display: table;
|
||||
width: 100%;
|
||||
}
|
||||
.left-col {
|
||||
display: table-cell;
|
||||
width: 65%;
|
||||
padding-right: 0.2in;
|
||||
vertical-align: top;
|
||||
}
|
||||
.right-col {
|
||||
display: table-cell;
|
||||
width: 35%;
|
||||
vertical-align: top;
|
||||
text-align: right;
|
||||
}
|
||||
.photo {
|
||||
width: 2in;
|
||||
height: auto;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
.title {
|
||||
font-size: 24pt;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.05in;
|
||||
}
|
||||
.subtitle {
|
||||
font-size: 12pt;
|
||||
font-style: italic;
|
||||
margin-bottom: 0.2in;
|
||||
color: #444;
|
||||
}
|
||||
h3 {
|
||||
font-size: 12pt;
|
||||
font-weight: bold;
|
||||
margin: 0.15in 0 0.08in 0;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05in;
|
||||
border-bottom: 1px solid #999;
|
||||
padding-bottom: 0.03in;
|
||||
}
|
||||
.personal-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.personal-table td {
|
||||
padding: 0.02in 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
.personal-table .label {
|
||||
font-weight: bold;
|
||||
width: 1.2in;
|
||||
color: #333;
|
||||
}
|
||||
.personal-table .header {
|
||||
font-weight: bold;
|
||||
font-size: 12pt;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05in;
|
||||
padding-bottom: 0.05in;
|
||||
}
|
||||
.section-header {
|
||||
font-weight: bold;
|
||||
font-size: 11pt;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03in;
|
||||
margin-top: 0.15in;
|
||||
color: #333;
|
||||
}
|
||||
ul {
|
||||
margin: 0.05in 0;
|
||||
padding-left: 0.15in;
|
||||
}
|
||||
li {
|
||||
margin-bottom: 0.04in;
|
||||
font-size: 10pt;
|
||||
}
|
||||
.job-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
.job-meta {
|
||||
font-style: italic;
|
||||
font-size: 10pt;
|
||||
}
|
||||
.job-company {
|
||||
font-weight: bold;
|
||||
}
|
||||
.job-desc {
|
||||
font-style: italic;
|
||||
font-size: 10pt;
|
||||
margin-left: 0.1in;
|
||||
}
|
||||
.edu-item {
|
||||
font-size: 10pt;
|
||||
margin-bottom: 0.03in;
|
||||
}
|
||||
.edu-school {
|
||||
font-style: italic;
|
||||
}
|
||||
.skill-category {
|
||||
font-weight: bold;
|
||||
font-size: 10pt;
|
||||
margin-top: 0.05in;
|
||||
}
|
||||
.skill-list {
|
||||
font-size: 10pt;
|
||||
margin-left: 0.1in;
|
||||
}
|
||||
.footer {
|
||||
font-size: 9pt;
|
||||
color: #666;
|
||||
margin-top: 0.3in;
|
||||
border-top: 1px solid #ccc;
|
||||
padding-top: 0.1in;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="left-col">
|
||||
<p class="title">Rafael Polo</p>
|
||||
<p class="subtitle">Senior Software Engineer — Platform, Data & AI</p>
|
||||
|
||||
<table class="personal-table">
|
||||
<tr>
|
||||
<td class="header" colspan="2">Personal</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Address:</td>
|
||||
<td>Basel, 4055, Switzerland</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Email:</td>
|
||||
<td>rafael@extrapolo.com</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">LinkedIn:</td>
|
||||
<td>linkedin.com/in/rafaelpolo</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Web:</td>
|
||||
<td>extrapolo.com</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Citizenship:</td>
|
||||
<td>Portuguese (EU) · Brazilian — no work permit required</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Languages:</td>
|
||||
<td>Fluent English · German B1 TELC (3 yrs Berlin + 3 yrs Basel) · EU citizen, immediately available</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h3>Formal Education</h3>
|
||||
<ul>
|
||||
<li><em><strong>Postgraduate in Data Science for Finance</strong></em> – 2021<br>
|
||||
<span class="edu-school">NOVA IMS Information Management School, Lisbon</span></li>
|
||||
<li><em><strong>Master in Information Systems</strong></em> – 2010–2012<br>
|
||||
<span class="edu-school">Research Area: Knowledge Representation & Reasoning, PPGI/UNIRIO</span></li>
|
||||
<li><em><strong>Bachelor in Computer Science</strong></em> – 2004–2009<br>
|
||||
<span class="edu-school">Metodista Bennett, Rio de Janeiro</span></li>
|
||||
<li>Thesis: Agile Systems Development for Web 2.0 with Ruby on Rails</li>
|
||||
</ul>
|
||||
|
||||
<h3>Relevant Professional Experience</h3>
|
||||
<ul>
|
||||
<li><strong>Swisscom</strong> – <em>2024–2025</em> – <em>Zürich, Switzerland</em>
|
||||
<p class="job-desc"><strong>DevOps Engineer</strong> developing platforms for Telecommunication Operations.</p></li>
|
||||
<li><strong>EDGE Empower®</strong> – <em>2024–2025</em> – <em>Baar, Switzerland</em>
|
||||
<p class="job-desc"><strong>Senior Technical Lead</strong> managing projects and external UX & Dev Teams for a SaaS Company.</p></li>
|
||||
<li><strong>ExtraPolo — Independent Consulting</strong> – <em>2010–2022</em> – <em>Rio de Janeiro · Berlin · Athens · Lisbon</em>
|
||||
<p class="job-desc">For more than 10 years, worked as an independent Software Developer and IT Consultant, developing dozens of Projects for public and private contractors in Brazil, Germany, Greece and Switzerland.</p></li>
|
||||
<li><strong>Moedax Digital Services</strong> – <em>2018–2021</em> – <em>Lisbon and Rio de Janeiro</em>
|
||||
<p class="job-desc"><strong>Founder & CTO</strong> – connected Brazilian CBDC with KYC processes and automation</p></li>
|
||||
<li><strong>GFOSS.eu / Code + Create</strong> – <em>2017–2018</em> – <em>Athens, Greece</em>
|
||||
<p class="job-desc"><strong>Web Programming Teacher</strong> – taught web programming to refugee and local youth</p></li>
|
||||
<li><strong>Tactical Tech Collective</strong> – <em>2014–2016</em> – <em>Berlin, Germany</em>
|
||||
<p class="job-desc"><strong>Full-Stack Web Architect</strong> on an NGO under the Exposing the Invisible Team.</p></li>
|
||||
<li><strong>Trommsdorf + Drüner</strong> – <em>2013–2014</em> – <em>Berlin, Germany</em>
|
||||
<p class="job-desc"><strong>Full-stack Engineer</strong> – Innovation Team developing Business Intelligence dashboards.</p></li>
|
||||
<li><strong>Cortex Intelligence</strong> – <em>2007–2008</em> – <em>Rio de Janeiro, Brazil</em>
|
||||
<p class="job-desc"><strong>Developer</strong> in the Competitive Intelligence and Data Mining Team.</p></li>
|
||||
</ul>
|
||||
|
||||
<h3>Certifications</h3>
|
||||
<ul>
|
||||
<li>Swiss Federal Act on Data Protection (nFADP)</li>
|
||||
<li>Gen AI Foundational Models — NLP & LLMs</li>
|
||||
<li>AI Agents using RAG and LangChain</li>
|
||||
</ul>
|
||||
|
||||
<h3>Relevant Tech Skills</h3>
|
||||
<ul>
|
||||
<li>System Analysis & Modeling with UML</li>
|
||||
<li>Quick Proof-of-Concepts development, deploy and management.</li>
|
||||
<li>Large Language Models, Gen AI, LangChain integration and Processes Automation</li>
|
||||
<li>MVC System Architecture, Design Patterns & Object-Oriented Programming</li>
|
||||
<li>Data Engineering, Data Science, Relational Databases & SQL</li>
|
||||
<li>Experience with Blockchain, Smart Contracts and P2P systems</li>
|
||||
<li>Distributing and Scaling Processes with Message Queues</li>
|
||||
<li>Developing Web Crawlers & Bots for Data Mining & ETL</li>
|
||||
<li>Linux Systems, VPS, DevOps, SSH & Deploy with Continuous Integration processes</li>
|
||||
<li>Projects mostly developed in Python, Ruby on Rails, Django, Java</li>
|
||||
<li>Full Stack Developer & Front-end Web Design with HTML, CSS and JavaScript frameworks</li>
|
||||
<li>CI/CD, Docker, Kubernetes and GitHub workflows</li>
|
||||
</ul>
|
||||
|
||||
<h3>Natural Languages</h3>
|
||||
<ul>
|
||||
<li>Native Brazilian Portuguese</li>
|
||||
<li>Advanced Spoken Spanish</li>
|
||||
<li>A2 level Greek – <em>3 years in Athens</em></li>
|
||||
<li>B1 level German / TELC proof – <em>3 years in Berlin</em> and 3 in Switzerland</li>
|
||||
<li>Fluent English – <em>13 years in between international teams</em></li>
|
||||
</ul>
|
||||
|
||||
<div class="footer">
|
||||
References available upon request · CV updated May 2026
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-col">
|
||||
<!-- Photo placeholder - remove if not needed -->
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-130
@@ -1,130 +0,0 @@
|
||||
Rafael Polo
|
||||
|
||||
Basel, 4055, Switzerland
|
||||
|
||||
rafael@extrapolo.com
|
||||
|
||||
linkedin.com/in/rafaelpolo
|
||||
|
||||
extrapolo.com
|
||||
|
||||
Senior Software Engineer — Platform, Data & AI
|
||||
|
||||
Swiss B permit · Portuguese (EU) citizen
|
||||
|
||||
20+ years building production systems across Switzerland, Germany, Greece and Brazil. Specialised in platform
|
||||
|
||||
engineering, data pipelines and AI/LLM integrations. Fluent English · German B1 TELC (3 yrs Berlin + 3 yrs Basel) · EU
|
||||
|
||||
citizen, immediately available.
|
||||
|
||||
P R O F E S S I O N A L E X P E R I E N C E
|
||||
|
||||
Swisscom
|
||||
DevOps Engineer · Zürich, Switzerland
|
||||
|
||||
Modernization of mission-critical internal platforms & CI/CD pipelines for high-availability Telecom infrastructure.
|
||||
|
||||
EDGE Empower®
|
||||
|
||||
Senior Technical Lead · Baar, Switzerland
|
||||
|
||||
Mar 2024 – Mar 2025
|
||||
|
||||
Feb 2023 – Feb 2024
|
||||
|
||||
Built a PayTool cutting processing time by 98%; reduced CI/CD pipeline from 14 → 4 minutes; managed 3 developers and a UX
|
||||
|
||||
designer for a SaaS product.
|
||||
|
||||
ExtraPolo — Independent Consulting
|
||||
Software Engineer & Consultant · Rio de Janeiro · Berlin · Athens · Lisbon
|
||||
|
||||
2010 – 2022
|
||||
|
||||
40+ projects across civic tech, data journalism, open-source platforms and interactive installations for public and private clients. Includes Rodado (~730 Brazilian public datasets, 1TB Parquet, DuckDB, NL-to-SQL over MCP) and HelvetiScan (scanner/mapper of 2.5M+ Swiss .ch domains — HTTP, DNS, TLS, WHOIS, CVE).
|
||||
|
||||
Moedax Digital Services
|
||||
|
||||
Founder & CTO · Lisbon · Rio de Janeiro
|
||||
|
||||
2018 – 2021
|
||||
|
||||
Fintech startup connecting CBDC digital currency with KYC compliance automation; built data pipelines and banking integrations in
|
||||
Python.
|
||||
|
||||
GFOSS.eu / Code + Create
|
||||
Web Programming Teacher · Athens, Greece
|
||||
|
||||
2017 – 2018
|
||||
|
||||
Taught web programming (HTML, CSS, jQuery) to refugee and local youth through the EU-funded Code + Create initiative.
|
||||
|
||||
Tactical Tech Collective
|
||||
Full-Stack Web Architect · Berlin, Germany
|
||||
|
||||
Dec 2014 – Jun 2016
|
||||
|
||||
Built investigative data tools and platforms under the Exposing the Invisible team (Ruby on Rails, data visualisation).
|
||||
|
||||
Trommsdorff + Drüner
|
||||
|
||||
Full-Stack Engineer · Berlin, Germany
|
||||
|
||||
Innovation team — Business Intelligence dashboards for marketing consultancy clients.
|
||||
|
||||
Cortex Intelligence
|
||||
Developer · Rio de Janeiro, Brazil
|
||||
|
||||
Competitive Intelligence and Data Mining R&D team.
|
||||
|
||||
E D U C A T I O N
|
||||
|
||||
L A N G U A G E S
|
||||
|
||||
Dec 2013 – Jan 2014
|
||||
|
||||
2008 – 2010
|
||||
|
||||
Postgraduate in Data Science for Finance
|
||||
NOVA IMS Information Management School, Lisbon — 2021
|
||||
|
||||
Master in Information Systems
|
||||
PPGI/UNIRIO — Knowledge Representation & Reasoning —
|
||||
2010–2012
|
||||
|
||||
Bachelor in Computer Science
|
||||
Metodista Bennett, Rio de Janeiro — 2004–2009
|
||||
|
||||
Portuguese — native
|
||||
|
||||
English — full professional
|
||||
|
||||
Spanish — full professional
|
||||
|
||||
German B1 — TELC certified · 3 yrs Berlin + 3 yrs Basel
|
||||
|
||||
C E R T I F I C A T I O N S
|
||||
|
||||
Swiss Federal Act on Data Protection (nFADP)
|
||||
|
||||
Gen AI Foundational Models — NLP & LLMs
|
||||
|
||||
AI Agents using RAG and LangChain
|
||||
|
||||
T E C H S K I L L S
|
||||
|
||||
Languages & Frameworks · Python · Ruby on Rails ·
|
||||
TypeScript · React
|
||||
|
||||
DevOps & Infra · Docker · Kubernetes · CI/CD · AWS · Linux ·
|
||||
Git
|
||||
|
||||
Data & AI · SQL · ETL · Data Engineering · RAG · LangChain ·
|
||||
LLM integrations · Data Science
|
||||
|
||||
Architecture · REST APIs · MVC · OOP · UML · System Design ·
|
||||
Web Crawling & Scraping
|
||||
|
||||
References available upon request · CV updated May 2026
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
// Daily watch for the "Delivery Lead – Agentic Solutions" opening (Biomedical
|
||||
// Research Digital & Data, likely Novartis) that MRP-Global posted on LinkedIn
|
||||
// as job 4473851998 on 2026-10-03. Looks for the same role resurfacing in
|
||||
// another form — another agency, a direct Novartis posting, a retitled
|
||||
// repost — and reports when the original stops accepting applications.
|
||||
//
|
||||
// Runs from launchd (com.rafapolo.linkme-watch-delivery-lead, daily 10:00).
|
||||
// Pushes to Telegram only when something new turns up; everything else goes to
|
||||
// results/watch-delivery-lead.log. State lives in results/.watch-delivery-lead.json,
|
||||
// so a hit is reported once. `--dry-run` prints instead of sending.
|
||||
|
||||
import { fetchDescription } from "../fetch.ts";
|
||||
import { send, escapeHtml } from "../telegram.ts";
|
||||
|
||||
const ORIGINAL_ID = "4473851998";
|
||||
const STATE_FILE = "results/.watch-delivery-lead.json";
|
||||
const DRY_RUN = process.argv.includes("--dry-run");
|
||||
|
||||
const UA =
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36";
|
||||
|
||||
// Searches cast wide; SIGNATURE decides. A repost of this opening reuses the
|
||||
// client's wording, so a candidate needs one client-specific phrase (STRONG)
|
||||
// plus one role phrase. "agentic" + "delivery lead" alone matched Eraneos and
|
||||
// AWS on the first dry run (2026-10-05) — generic consultancy wording.
|
||||
const LINKEDIN_QUERIES = [
|
||||
"delivery lead agentic",
|
||||
"delivery lead AI data pharma",
|
||||
"delivery lead biomedical",
|
||||
"agentic solutions delivery",
|
||||
"biomedical research digital data",
|
||||
"delivery manager agentic",
|
||||
];
|
||||
const NOVARTIS_QUERIES = ["delivery lead", "delivery manager", "agentic", "biomedical research digital"];
|
||||
|
||||
const SIGNATURE: [string, RegExp][] = [
|
||||
["agentic", /\bagentic\b/i],
|
||||
["Biomedical Research", /biomedical\s+research/i],
|
||||
["Digital & Data", /digital\s*(?:&|and)\s*data/i],
|
||||
["delivery lead", /delivery\s+lead/i],
|
||||
["Palantir", /\bpalantir\b/i],
|
||||
["Databricks", /\bdatabricks\b/i],
|
||||
["RWD", /\bRWD\b|real[-\s]world\s+data|clinical\s+trials?/i],
|
||||
];
|
||||
const STRONG = new Set(["Biomedical Research", "Digital & Data", "Palantir", "RWD"]);
|
||||
const ROLE = new Set(["agentic", "delivery lead"]);
|
||||
const isMatch = (m: string[]) => m.some((x) => STRONG.has(x)) && m.some((x) => ROLE.has(x));
|
||||
const TITLE_RE = /deliver|agentic|programme?\s+lead|project\s+lead/i;
|
||||
|
||||
interface Hit { key: string; title: string; company: string; link: string; matched: string[] }
|
||||
interface State { seen: string[]; reported: string[]; originalClosed?: boolean }
|
||||
|
||||
async function loadState(): Promise<State> {
|
||||
const f = Bun.file(STATE_FILE);
|
||||
return (await f.exists()) ? await f.json() : { seen: [], reported: [] };
|
||||
}
|
||||
|
||||
function signature(text: string): string[] {
|
||||
return SIGNATURE.filter(([, re]) => re.test(text)).map(([name]) => name);
|
||||
}
|
||||
|
||||
const strip = (s: string) =>
|
||||
s.replace(/<[^>]+>/g, " ").replace(/&/g, "&").replace(/'|'/g, "'").replace(/\s+/g, " ").trim();
|
||||
|
||||
async function get(url: string): Promise<string> {
|
||||
const res = await fetch(url, { headers: { "User-Agent": UA } });
|
||||
if (!res.ok) throw new Error(`${res.status} ${url}`);
|
||||
return res.text();
|
||||
}
|
||||
|
||||
async function searchLinkedIn(state: State): Promise<Hit[]> {
|
||||
const cards = new Map<string, { title: string; company: string }>();
|
||||
for (const q of LINKEDIN_QUERIES) {
|
||||
// Past month, Switzerland-wide. One page is enough: these queries are narrow.
|
||||
const url = `https://www.linkedin.com/jobs-guest/jobs/api/seeMoreJobPostings/search?keywords=${encodeURIComponent(q)}&location=Switzerland&f_TPR=r2592000&start=0`;
|
||||
try {
|
||||
const html = await get(url);
|
||||
for (const c of html.split("<li>").slice(1)) {
|
||||
const id = c.match(/jobs\/view\/[^"]*?(\d{8,})/)?.[1];
|
||||
const title = strip(c.match(/base-search-card__title[^>]*>([\s\S]*?)<\/h3>/)?.[1] ?? "");
|
||||
const company = strip(c.match(/base-search-card__subtitle[^>]*>([\s\S]*?)<\/h4>/)?.[1] ?? "");
|
||||
if (id && id !== ORIGINAL_ID && TITLE_RE.test(title)) cards.set(id, { title, company });
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(` linkedin "${q}": ${e}`);
|
||||
}
|
||||
await Bun.sleep(1500);
|
||||
}
|
||||
|
||||
const hits: Hit[] = [];
|
||||
for (const [id, { title, company }] of cards) {
|
||||
const key = `li-${id}`;
|
||||
if (state.seen.includes(key)) continue;
|
||||
const desc = await fetchDescription(id);
|
||||
if (desc === null) continue; // unreadable — retry tomorrow rather than mark seen
|
||||
state.seen.push(key);
|
||||
const matched = signature(`${title} ${desc}`);
|
||||
console.log(` li ${id} ${title} @ ${company}: [${matched.join(", ")}]`);
|
||||
if (isMatch(matched)) {
|
||||
hits.push({ key, title, company, link: `https://www.linkedin.com/jobs/view/${id}/`, matched });
|
||||
}
|
||||
}
|
||||
return hits;
|
||||
}
|
||||
|
||||
async function searchNovartis(state: State): Promise<Hit[]> {
|
||||
const found = new Map<string, { title: string; path: string }>();
|
||||
for (const q of NOVARTIS_QUERIES) {
|
||||
const url = `https://www.novartis.com/careers/career-search?search_api_fulltext=${encodeURIComponent(q)}&country%5B%5D=LOC_CH`;
|
||||
try {
|
||||
const html = await get(url);
|
||||
for (const m of html.matchAll(/href="(\/careers\/career-search\/job\/details\/(req-\d+)[^"]*)"[^>]*>([\s\S]*?)<\/a>/g)) {
|
||||
const title = strip(m[3]);
|
||||
if (TITLE_RE.test(title)) found.set(m[2], { title, path: m[1] });
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(` novartis "${q}": ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
const hits: Hit[] = [];
|
||||
for (const [req, { title, path }] of found) {
|
||||
const key = `nv-${req}`;
|
||||
if (state.seen.includes(key)) continue;
|
||||
const link = `https://www.novartis.com${path}`;
|
||||
let desc: string;
|
||||
try {
|
||||
desc = strip(await get(link));
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
state.seen.push(key);
|
||||
const matched = signature(`${title} ${desc}`);
|
||||
console.log(` nv ${req} ${title}: [${matched.join(", ")}]`);
|
||||
if (isMatch(matched)) hits.push({ key, title, company: "Novartis", link, matched });
|
||||
}
|
||||
return hits;
|
||||
}
|
||||
|
||||
async function originalClosed(): Promise<boolean | null> {
|
||||
try {
|
||||
const html = await get(`https://www.linkedin.com/jobs/view/${ORIGINAL_ID}/`);
|
||||
return /no longer accepting applications/i.test(html);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const state = await loadState();
|
||||
console.log(`[${new Date().toISOString()}] watch-delivery-lead`);
|
||||
|
||||
const hits = [...(await searchLinkedIn(state)), ...(await searchNovartis(state))].filter(
|
||||
(h) => !state.reported.includes(h.key),
|
||||
);
|
||||
const closed = await originalClosed();
|
||||
|
||||
const lines: string[] = [];
|
||||
for (const h of hits) {
|
||||
lines.push(
|
||||
`• <b>${escapeHtml(h.title)}</b> — ${escapeHtml(h.company)}\n ${h.link}\n matched: ${escapeHtml(h.matched.join(", "))}`,
|
||||
);
|
||||
}
|
||||
if (closed === true && !state.originalClosed) {
|
||||
lines.push(`• The original MRP-Global posting (${ORIGINAL_ID}) is no longer accepting applications.`);
|
||||
}
|
||||
|
||||
if (lines.length) {
|
||||
const msg = `🔎 <b>Delivery Lead – Agentic Solutions watch</b>\n\n${lines.join("\n\n")}`;
|
||||
if (DRY_RUN) console.log(msg);
|
||||
else await send(msg);
|
||||
}
|
||||
|
||||
// Only mark as reported after a successful send, same rule as daily.ts.
|
||||
state.reported.push(...hits.map((h) => h.key));
|
||||
if (closed === true) state.originalClosed = true;
|
||||
if (!DRY_RUN) await Bun.write(STATE_FILE, JSON.stringify(state, null, 2));
|
||||
console.log(` ${hits.length} new hit(s), original closed: ${closed}`);
|
||||
Reference in New Issue
Block a user