EcomConsulting

outreach-daily

Use when someone asks to run or manage AI-led outreach, prospect larger Swedish companies, prepare a daily outreach batch, or update the partner outreach process shared with Sebastian and Stina. Triggas också av "dagens 20", "kör outreach", "kör dagens outreach", "morgonbatch", "dra igång dagens utskick" eller /outreach-daily.

EcomConsulting 0 Updated 1w ago

Resources

7
GitHub

Install

npx skillscat add ecomconsulting/outreach-daily

Install via the SkillsCat registry.

SKILL.md

AI-led outreach — granskningsbar batch utan förarbete för partnerteamet

Delad process för Mikael, Stina och Sebastian. AI kör kedjan i Mikaels
workspace och tar fram en granskningsbar batch. Mikael godkänner den faktiska
batchen före utskick. Sebastian och Stina kopplas inte in förrän ett prospekt
har svarat positivt. Allt bygger på VERKLIG mätdata.

Bindande ansvarsfördelning: läs references/operating-model.md före varje
körning. Den filen styr vem som gör vad och ersätter den äldre modellen med tre
dagliga operatörer.

Sluttillstånd: godkänd konfig → hela kedjan Apollo-pull → benchmark → copy →
review-sheet körs till granskningsbar sheet i ETT svar, med [steg X/Y]-statusrader.
Mikael ska inte behöva säga "fortsätt" mellan stegen — nästa beslutspunkt är
"skicka" på den färdiga sheeten. (Undantag: filtergodkännande vid ny eller
ändrad konfiguration samt stoppen i steg 1/2 vid credit-brist eller struken gate
gäller fortfarande.)

Steg 0 — hämta senaste versionen (varje körning, före allt annat)

Copy-kanon, operating model och mätpunkter ändras löpande. Kör därför alltid
detta först, från skill-mappen:

git pull --ff-only

Kör pullen bara om skill-mappen själv är en klon av det delade repot:

git -C <skill-mappen> remote get-url origin   # ska ge github.com/EcomConsulting/outreach-daily

Ger det något annat, eller fel, är mappen Mikaels arbetskopia i ~/HQ (den
ÄR källan och ligger i ett repo utan remote) — då hoppas steget över utan att
köra git pull. Rapportera alltid utfallet för Mikael — "redan
aktuell" eller vilka filer som ändrades — så det är känt vilken kanon dagens
batch kördes på. Ändrades references/copy-template.md, läs om den innan
copyn skrivs. CHANGELOG.md säger vad som ändrats och varför.

Din egen config.json ligger utanför repot och rörs aldrig av en pull.

Förutsättningar (kolla FÖRST, en gång per installation)

  1. config.json finns i skill-mappen. I den aktuella AI-ledda partnerprocessen
    är operator alltid mikael, eftersom Mikael är extern avsändare och
    godkännare även när AI utför arbetet. copy_segment är obligatorisk:
    ecommerce för e-handelsprospekt eller traditional-b2b för traditionella
    B2B- och industribolag — saknas nyckeln stannar build_sheet.py (ingen tyst
    default). Byt segment i config innan en batch i ny kategori körs. Fyll i
    kategori och Apollo-filter för den godkända batchen.
  2. APOLLO_API_KEY finns i miljön eller i projektets .env. Nyckeln delas av
    teamet (Sebastians konto). Saknas den: stanna och be om den.
  3. python3 finns (skripten kräver bara standardbiblioteket).
  4. Google Sheets-åtkomst: gws CLI om installerad. Saknas gws faller steg 4
    tillbaka till en CSV som AI lämnar för manuell import till Sheets.
  5. För automatiskt utskick (steg 5): sender_email + signature + send_path
    ifyllda i config.json, och avsändarkontot nåbart via EN av vägarna —
    gws inloggad som avsändaradressen, ELLER claude.ai:s Gmail-connector
    kopplad till den. Finns ingendera stannar AI vid den godkända utskicksplanen.

Dubblettskyddet (varför ingen kan skicka till samma bolag)

  • AI använder teamets gemensamma Apollo-konto. Pull-skriptet filtrerar på
    prospected_by_current_team = no och sparar varje hämtad person som kontakt
    — nästa pull ser dem aldrig igen.
  • Prospekteringen prioriterar större bolag inom bygg, el, stål, fastigheter,
    industri och närliggande branscher. Filter för storlek och bransch ska visas
    vid ny eller ändrad konfiguration och godkännas innan Apollo-krediter används.

Steg för steg

1. Lista — 20 nya prospekt ur Apollo

python3 scripts/apollo_pull.py            # läser config.json, default 20
python3 scripts/apollo_pull.py --n $ARGUMENTS   # om skillen anropades med ett antal

Output: <output_dir>/YYYY-MM-DD-prospekt.csv — namn, titel, bolag, domän,
e-post, LinkedIn. Skriptet hämtar bara personer med upplåst e-post; varje rad
drar en Apollo-kredit. Svarar API:t "insufficient credits": stanna och
rapportera — hitta ALDRIG på adresser och fall aldrig tillbaka till rollkonton
(info@/kontakt@) utan Mikaels explicita OK per rad.

AI väljer större relevanta bolag enligt references/operating-model.md och
redovisar de faktiska Apollo-filtren före pull. Använd inte den gamla
uppdelningen där Mikael, Sebastian och Stina hade varsin fasta kategori.

2. Mät — punkt för punkt på mottagarens egen sajt

AI bygger en kluster-fil av dagens domäner ur -prospekt.csv, grupperade per
nisch. Klustret är bara en gruppering för körningen, inte en konkurrentjämförelse.
Format och filnamn:

<output_dir>/YYYY-MM-DD-kluster.json   →   {"nisch": ["domän.se", ...], ...}

Domäner som redan i klassificeringen visar sig ligga utanför kategorin (fel
produkttyp, tom katalog) lämnas utanför klustret och räknas som strukna.

Gaten (beslut Mikael 2026-08-19): en rad är skickbar när domänen saknar
minst tre av de mätta punkterna. Färre än tre saknade punkter → raden stryks.
Det finns ingen alternativ stomme och ingen nödmall.

Konkurrenter behövs inte längre och nämns aldrig i copyn. benchmark.py räknar
fortfarande ut losses (saknad punkt som majoriteten i klustret har) som intern
kontext — den siffran får aldrig gå till ett mejl. Det som styr copyn är
missing.

python3 scripts/benchmark.py --input <klusterfil.json> --profile <profil> --outdir <output_dir>

Profiler: shopify-dtc (e-postfångst, kundomdömen på PDP, Product-schema,
prenumeration på PDP, minst tre produktbilder, produkttext över 300 tecken) och
leadgen (kontakt-/offertformulär, synliga omdömen, schema-markup, H1). Output:
-benchmark-raw.csv + -benchmark-summary.json med missing och n_missing
per domän. Skriptet skriver ut hur många av dagens domäner som klarar gaten.

Mätdisciplin (hård gräns, ändras aldrig):

  • Ett fynd = domänen saknar punkten. Punkt.
  • Inga påhittade fynd. Varje fynd i copy ska gå att peka på i summary-filen.
  • Påstå aldrig frånvaro av sådant som inte kan mätas i statisk HTML.
  • Ser ett resultat orimligt ut (t.ex. stor aktör som "saknar" något):
    verifiera manuellt på fler undersidor innan det används i copy.
  • reviews-punkten får aldrig gå till copy utan kontroll i renderad DOM.
    Statisk HTML skiljer inte på tre olika lägen: omdömen som faktiskt visas,
    en installerad omdömesmodul som står tom ("0 recensioner", "Inga recensioner
    ännu"), och rena platshållare i temat. Alla tre mäts som True eller False på
    fel grunder. Kontrollprov 2026-08-19 i smyckesklustret: mulicollection.com
    mättes True men visar ingenting alls i webbläsaren, susannafalken.com och
    carolinesvedbom.com mättes True men står på noll omdömen. Öppna produktsidan
    i Playwright och läs sidan innan punkten används som fynd — åt båda hållen,
    både hos mottagaren och hos konkurrenterna som påstås ha den.
  • Rader med pdp_ok=False är mätfel, inte fynd — stryk dem först av allt.
    Går /products.json inte att hämta faller reviews, product_schema,
    images_3plus och desc_300plus alla ut som False utan att ha mätts, och
    domänen ser ut som dagens starkaste prospekt med n_missing=5. Tre orsaker:
    lösenordsskyddad butik (/password, HTTP 401), headless Shopify
    (Hydrogen/Oxygen — products.json finns inte), botblockering. Kontrollprov
    2026-08-22: silvercut.com mättes till fem saknade punkter men hade i
    renderad DOM 172 omdömen, Product-schema och 16 produktbilder. Filtrera på
    pdp_ok i -benchmark-raw.csv INNAN gaten räknas. Vill du behålla domänen
    måste den mätas om i renderad DOM.
  • En punkt som inte är en säljhävstång i mottagarens kategori är inget fynd.
    sub_pdp (prenumeration på produktsidan) saknades hos 20 av 20 domäner i
    smyckeskategorin 2026-08-22 — ingen prenumererar på ett halsband. Kanon kräver
    att varje fynd rimligen kan kopplas till att sälja mer, så punkten räknas inte
    i smycken, klockor, glasögon eller parfym. Räkna om n_missing utan den innan
    gaten tillämpas, och notera i rapporten hur många rader det kostade.

3. Copy — enligt kanon

Läs references/copy-template.md och skriv en rad per mottagare. AI skriver
resultatet till <output_dir>/YYYY-MM-DD-berikad.csv: alla kolumner ur
-prospekt.csv (Bolag, Mottagare, E-post, Alt-adress …) plus Nisch, Fynd
(exakt tre, semikolonseparerade, samma ordning som i mejlet), Ämnesrad och
Copy (hela mejltexten inklusive signatur). Strukna rader tas inte med alls.
Den filen är input till steg 4.

  • Skicka eller stryk: inga andra utfall får gå till review-sheeten.
  • n_missing ≥ 3 → skicka. Välj de tre fynd som är enklast att visa på skärm
    och tydligast kopplade till att sälja mer, och skriv in dem i Fynd.
  • n_missing < 3 → stryk raden. Skriv aldrig "två saker".
  • Den aktuella AI-ledda partnerprocessen använder Mikael-varianten och
    operator: mikael. Sebastian- och Stina-varianterna är historiska profiler,
    inte tillstånd att tilldela dem daglig outreach.
  • Segmentlås: Mikaels mening om cáene, True Religion, Primus Equipment och
    Silva Sweden används endast när copy_segment: ecommerce. Bygg, el, stål,
    fastigheter, industri och annan traditionell B2B använder alltid
    copy_segment: traditional-b2b och det separata introt i copymallen.
  • Mallen använder Vi har tittat närmare på ert bolag och hittat tre saker som vi gärna vill visa er, sälja mer och 10–15 minuters skärmdelning.
    Följ den valda mallen ordagrant; fyll bara dess faktaslots.
  • Konkurrenter nämns aldrig i mejlet. Ingen bransch-N, inga X av era Y.
  • Komma, inte tankstreck. Facktermer förklaras vid första användning.
  • CTA-tider enligt rullande regeln i mallen: utskicksdag +3 dagar kl 10:00,
    +2 vardagar kl 14:00 (helg rullar fram till vardag). Samma par för hela dagen.
  • Avsluta med endast tidsfrågan i mallen. Lägg inte till kalenderinbjudan eller
    möteslänk i första mejlet.
  • Ämnesraden finns i den bindande mallen.

4. Review-sheet

python3 scripts/build_sheet.py --csv <berikad.csv> --title "Outreach <operatör> YYYY-MM-DD"

Kolumner: Nr, Bolag, Mottagare, E-post, Alt-adress, Nisch, Fynd,
Ämnesrad (förslag), Copy, Godkänd, Skickat, Utfall. Fynd innehåller exakt
tre
semikolonseparerade fynd — samma tre som mejlet lovar. build_sheet.py
stoppar hela bygget om en rad saknar nisch, har annat än tre fynd, bryter det
bindande copykontraktet, korsar operatörsprofil eller nämner konkurrenter. Den
valda profilen läses från operator och copy_segment i config.json. Med gws skapas sheeten
direkt; utan gws behålls CSV:n för manuell import.

Godkänd är en förkryssad kryssruta (beslut Mikael 2026-08-18). Skriptet sätter
en riktig BOOLEAN-validering på hela kolumnen och kryssar i varje rad. Granskningen
blir därmed en utkryssning: Mikael läser raderna och kryssar UR det som inte
ska gå, i stället för att skriva JA på varje rad som ska gå. En urkryssad rad gråas
ut och stryks över i hela sin bredd, så bortfallet syns direkt.

Två fällor, båda hanterade i build_sheet.py — bryts de blir rutan tom eller
oklickbar: skrivningen måste ske med valueInputOption: USER_ENTERED (annars landar
"TRUE" som text), och valideringen måste sättas i ett batchUpdate EFTER att värdena
skrivits.

5. Granska → godkänn → skicka

AI går igenom varje rad, faktaverifierar de tre fynden och bifogar underlaget.
Mikael granskar mottagare, ämnesrad och exakt copy utan att göra om researchen,
och kryssar UR rutan i Godkänd på de rader som inte ska skickas. Allt som
fortfarande är ikryssat
går ut. Textläget lever kvar parallellt för sheets byggda före 2026-08-18 och för
den som hellre skriver: JA / OK / X godkänner, NEJ avslår.

Rollkonton kan inte godkännas med en kryssruta. Adresser som info@, order@,
hej@ m.fl. stoppas alltid av send_approved.py, även när rutan är ikryssad, och
listas separat i dry-run. För att ändå skicka till dem krävs ett aktivt beslut:
JA ROLLKONTO i textläget, eller flaggan --allow-role på körningen. Standardlinjen
är att inte skicka till dem alls (beslut Mikael 2026-08-17: "kommer ge noll").

När Mikael säger "skicka" kör AI:

python3 scripts/send_approved.py --sheet <spreadsheetId>          # dry-run, visa planen
python3 scripts/send_approved.py --sheet <spreadsheetId> --send   # avfyra

Bara rader med Godkänd + tom Skickat väljs; Skickat stämplas i sheeten efter
varje utskick. Två vägar, styrs av send_path i config.json:

  • gws (default): skriptet skickar självt från operatörens Gmail via
    gws gmail +send. Avsändar-gate: gws-kontot måste matcha sender_email,
    annars vägrar skriptet — fel konto kan aldrig avfyra en batch.
  • connector: avsändarkontot nås via claude.ai:s Gmail-connector i
    stället för gws (t.ex. Mikael: gws = caene, ecomconsulting = connector).
    Claude skickar då raderna själv via connectorn — dry-run-listan är
    arbetslistan, ämnesrad ur Ämnesrad-kolumnen, body = Copy + signature ur
    config — och stämplar Skickat direkt efter varje skickat mejl:
    python3 scripts/send_approved.py --sheet <id> --stamp <radnr,...>.
    --send är avstängt i detta läge. Förutsätter att connectorn är kopplad
    till samma adress som sender_email — verifiera vid installationen genom
    ett testmejl till operatörens egen adress och läs avsändaren i tråden.

Kör alltid dry-run först och visa Mikael utfallet innan något skickas.

6. Positivt svar → partneröverlämning → möte

Vid positivt svar: logga utfallet och koppla först då in Sebastian och Stina för
fördjupad analys eller mötesmedverkan vid behov. Driv mot bokat möte med två
konkreta tider. När mötet bokats görs de
tre fynd som mejlet lovar visningsbara på skärm — de står redan i sheetens
Fynd-kolumn och får inte bytas ut mot generiska råd. Hittas fler punkter under
förberedelsen är de en bonus i mötet, inte en ersättning. För in Utfall i
sheeten löpande.

Guardrails

  • Inget utskick utan Godkänd + "skicka"send_approved.py --send körs
    först när Mikael har gått igenom sheeten OCH sagt "skicka". Förkryssade
    rutor är ingen genväg förbi granskningen: de sparar skrivandet, inte läsandet.
    Har Mikael inte sagt "skicka" är ikryssade rader bara ett utkast.
    Ändra aldrig copy i en rad efter att den godkänts.
  • Rollkonton (info@/kontakt@/order@) skickas inte till utan explicit OK per rad.
  • Apollo-krediter kostar pengar — hämta aldrig fler än dagens N utan att fråga.
  • Ingen partnerarbetsbörda före positivt svar: Sebastian och Stina ska inte
    göra research, copygranskning, presentation eller offert innan mottagaren har
    visat intresse.
  • Split utan påhitt: en ny kund från processen omfattas av en procentuell
    split, men procentsats, beräkningsgrund och tidsperiod måste beslutas
    skriftligt innan avräkning eller utbetalning.
  • Ingen nödmall: hittas färre än tre fynd stryks prospektet. Skriv aldrig
    copy om varför researchen misslyckades och aldrig ett annat antal än tre.
  • API-nyckeln ligger i .env/miljön, aldrig i filer som delas eller committas.
  • Misslyckas ett steg: diagnostisera och rapportera orsaken, hoppa aldrig tyst
    över mätningen eller fyll i gissade värden.

Filer i denna skill

  • scripts/apollo_pull.py — steg 1 (lista + adresser)
  • scripts/benchmark.py — steg 2 (mätning, två profiler, missing styr copyn)
  • scripts/build_sheet.py — steg 4 (review-sheet)
  • scripts/send_approved.py — steg 5 (utskick av godkända rader via gws)
  • references/copy-template.md — copy-kanon (steg 3), bindande
  • references/operating-model.md — roller, överlämning och splitprincip, bindande
  • config.example.json — mall för operatörens config
  • tests/test_outreach_contract.py — kontraktstester; kör efter varje ändring
    i skript eller kanon: python3 -m unittest discover -s tests -v (stdlib,
    ingen pytest behövs)

Uppdatering hos Sebastian och Stina

  1. Klona repot rakt in i skill-mappen:
    git clone https://github.com/EcomConsulting/outreach-daily.git ~/.claude/skills/outreach-daily
    (Eller till projektets .claude/skills/ om du hellre kör per projekt.)
  2. Kör git pull --ff-only och läs references/operating-model.md.
  3. Sebastian och Stina behöver inte konfigurera Apollo, Gmail eller en daglig
    kategori. De kopplas in av Mikael först efter ett positivt svar.
  4. En ny session eller omstart kan krävas för att skillkatalogen ska läsa den
    uppdaterade beskrivningen.

Uppdateringar

Repot är enda källan. Mikael publicerar ändringar därifrån; alla andra hämtar
dem med git pull i skill-mappen (steg 0 gör det automatiskt vid varje
körning). Zip-utskick är avvecklat — det var så copy-kanon hann driva isär
mellan operatörerna en gång.