| name | location-enrichment |
| description | Standardize positions.location free-text into structured loc_*/work_*/role_family columns BEFORE marking any position as `checked`. Covers 10 special cases (Europe Remote, Italy+remote, multi-location, US-entity-in-EU). Enforces one-position-at-a-time, peer-aligned vocabulary, never-NULL work_country. Use whenever the Analyst is about to set status=checked on a position. |
| allowed-tools | Bash(python3 *), Bash(curl *), Bash(jq *), WebSearch |
location-enrichment — playbook strutturazione location + role_family
L'Analista popola 11 colonne della tabella positions PRIMA di
marcare status=checked. Mai lasciare una position checked senza
location enrichment.
Le 11 colonne da popolare
role_family text categoria semantica del ruolo
loc_city text città di ufficio (NULL se solo country)
loc_region text regione/stato (opzionale)
loc_country text paese fisico ufficio (NULL se solo continent)
loc_country_code text ISO-3166 alpha-2: IT, IE, HU, ...
loc_continent text Europe | Asia | Americas | Africa | Oceania
work_mode text onsite | hybrid | remote
work_country text paese contrattuale (entity che firma) — MAI NULL
work_country_code text ISO-2 del work_country
is_multi_location bool true se JD elenca più città/paesi
location_notes text note libere analista
REGOLE comportamentali (CRITICHE — sim 1-2 ha trovato problemi qui)
R1 — Una posizione alla volta (NO BATCH)
Processa il tuo range una posizione per turno: leggi JD → ragiona →
db-update → status=checked → prossima. NIENTE caricare 20+ JD in un
unico turno LLM. Eccezione: 3-5 casi banali senza web search (es.
"Dublin, Ireland" + hybrid).
Perché: batch da 17k+ tokens (sim 1) genera responses generiche
("multi-location + remote + EU") invece di dati specifici per ogni
record. E gli altri analisti girano a vuoto durante il tuo mega-turn.
R2 — Peer DB lookup tassonomia (ogni 5-10 record)
PRIMA di scegliere un valore role_family, controlla cosa hanno usato
i colleghi:
python3 /app/shared/skills/db_query.py raw \
"SELECT role_family, COUNT(*) AS n FROM positions
WHERE role_family IS NOT NULL
GROUP BY role_family ORDER BY n DESC"
Se trovi una family semanticamente equivalente, ALLINEATI al loro
nome. Esempi sbagliati visti in sim 1:
✗ "Translation / Localization" vs "Localization / Language Quality"
vs "Language / Localization" → uno solo
✗ "Customer Support" vs "Customer Success / Technical"
vs "Technical Support" → uno solo
✗ "Technical Engineering" per un Technical Writer → wrong
Se la posizione è davvero una nuova categoria, annota in
location_notes perché.
R3 — Fallback work_country (MAI NULL su checked)
Se dopo 2 tentativi di web search non trovi work_country con
certezza, NON lasciarlo NULL. Procedi:
- Paese del posting board (es. linkedin.it → IT) + nota
"work_country inferred from posting board (low confidence)"
- Paese citato in JD come "region" / "office" anche se non sede legale
- Ultima istanza: il
loc_continent come placeholder + nota
"work_country=Europe placeholder, entity unverified"
R4 — Peer DB lookup città (PRIMA di scrivere loc_city)
Esattamente come R2 per role_family, ma per le città. PRIMA di
scrivere loc_city, controlla quale forma i colleghi hanno già usato
per quel paese, così non crei un duplicato in altra lingua
(Rome vs Roma, Milan vs Milano):
python3 /app/shared/skills/db_query.py raw \
"SELECT loc_country, loc_city, COUNT(*) AS n FROM positions
WHERE loc_city IS NOT NULL
GROUP BY loc_country, loc_city ORDER BY loc_country, n DESC"
- Se la città è già presente in una forma → ALLINEATI a quella
(purché rispetti lo standard "esonimo inglese", vedi sotto).
- Se vedi un duplicato in lingua diversa già nel DB (es. esiste sia
Roma sia Rome), usa la forma inglese e annota in
location_notes la forma da consolidare.
Standard di scrittura
Paesi (loc_country / work_country)
| Sì ✓ | No ✗ |
|---|
Italy | Italia, IT, Italie |
United Kingdom | UK, Great Britain, England |
Czechia | Czech Republic |
Netherlands | Holland, The Netherlands |
Székesfehérvár | Szekesfehervar (preserva sempre i diacritici) |
ISO-2 IT, IE, HU, NL, DE, GB, US, ES | ISO-3, lowercase |
Città (loc_city) — esonimo INGLESE quando esiste
Regola unica: scrivi sempre la forma inglese della città quando
esiste un esonimo consolidato. Se la città NON ha un esonimo inglese,
usa il nome locale preservando i diacritici. Questo allinea
l'Analista alla mappa di dedup dello Scout (_CITY_SYNONYMS in
shared/skills/db_insert.py) ed elimina i duplicati Rome/Roma,
Milan/Milano.
| Sì ✓ (esonimo EN) | No ✗ (forma locale) |
|---|
Rome | Roma |
Milan | Milano |
Naples | Napoli |
Turin | Torino |
Florence | Firenze |
Venice | Venezia |
Genoa | Genova |
Munich | München, Monaco di Baviera |
Cologne | Köln |
Vienna | Wien |
Prague | Praha |
Brussels | Bruxelles |
Lisbon | Lisboa |
Plzeň (no esonimo → locale + diacritici) | Plzen |
In dubbio sull'esistenza di un esonimo consolidato, applica il peer DB
lookup (R4) e allineati alla forma già presente per quella città.
Casi speciali (decisione standard)
A — "Europe Remote" / "EMEA - Flexible" / "Remote"
loc_city = NULL
loc_country = NULL # niente paese fisico vincolato
loc_continent = "Europe" # solo se area è esplicita
work_mode = "remote"
work_country = <web search HQ azienda → fallback R3>
is_multi_location = false
location_notes = "Remote within EU"
B — "Italy" / "Spain" + full_remote (paese + remote)
loc_country = "Italy"
loc_country_code = "IT"
loc_continent = "Europe"
work_mode = "remote"
work_country = "Italy" # stesso paese, contratto IT
work_country_code = "IT"
C — "Dublin, Ireland" + hybrid (city+country pulito)
loc_city = "Dublin"
loc_region = "Leinster" # opzionale
loc_country = "Ireland"
loc_country_code = "IE"
loc_continent = "Europe"
work_mode = "hybrid"
work_country = "Ireland"
work_country_code = "IE"
D — Multi-location stesso paese ("Barcelona / Malaga")
loc_city = NULL
loc_country = "Spain"
loc_country_code = "ES"
loc_continent = "Europe"
work_mode = "hybrid"
work_country = "Spain"
is_multi_location = true
location_notes = "Barcelona or Málaga (candidato sceglie)"
E — Multi-country ("Amsterdam, Berlin, London, Remote-Europe")
loc_city = NULL
loc_country = NULL
loc_continent = "Europe"
work_mode = "hybrid" # o remote
work_country = <HQ azienda via web>
is_multi_location = true
location_notes = "EU multi-country: NL, DE, GB + remote option"
F — Area metropolitana vaga ("Greater Bologna Metropolitan Area")
loc_city = "Bologna" # promuovi alla città principale
loc_country = "Italy"
location_notes = "Area metropolitana Bologna (raggio ~30km)"
G — Azienda US con entity EU che assume in Spagna
loc_country = NULL
loc_continent = "Europe"
work_mode = "remote"
work_country = "Spain" # entity locale che firma
location_notes = "US company (X Inc.), assume tramite entity ES"
H — JD precisa città che lo scout aveva generalizzato
Scout aveva scritto "Italy" → JD nel testo specifica "Milano HQ":
promuovi a città.
loc_city = "Milan"
loc_country = "Italy"
location_notes = "JD specifica HQ Milano (scout aveva 'Italy')"
I — City abbreviata ("Dublin 2")
loc_city = "Dublin"
loc_region = "Dublin 2" # distretto in region
J — Azienda solo job board (Railsware, Top Remote Talent, ecc.)
Quando l'azienda è una società distribuita senza HQ chiaro:
applica fallback R3 (paese del posting board) + annota.
Vietati assoluti
- ❌
loc_country = "Europe" o "EMEA" — è continent, non country
- ❌ Mappare "EMEA" come "Europe" senza controllo (include Middle East + Africa)
- ❌
work_country = NULL su una position checked (rompe UI stipendio)
- ❌ Inventare role_family se i colleghi hanno già usato simili → vedi R2
- ❌ Scrivere
loc_city in lingua locale quando esiste l'esonimo
inglese (Roma, Milano, Napoli → usa Rome, Milan, Naples)
o senza peer DB lookup → vedi R4 + tabella città
- ❌ Caricare il batch intero del proprio range → vedi R1
- ❌
loc_city = "Remote" / "Anywhere" / "Distributed" — NON sono città.
Se la position è full-remote senza city specifica, loc_city = NULL.
Bug osservato in sim 4: A2 ha scritto loc_city='Remote' per 8 record
(Canonical, Miratech, Link Group, ecc.). Correggi sempre con
db_update --loc-city "" (stringa vuota = NULL).
Comandi tipo
Salvataggio struttura location completa
python3 /app/shared/skills/db_update.py position <ID> \
--loc-city "Dublin" \
--loc-region "Leinster" \
--loc-country "Ireland" --loc-country-code "IE" \
--loc-continent "Europe" \
--work-mode "hybrid" \
--work-country "Ireland" --work-country-code "IE" \
--is-multi-location false \
--role-family "Technical Writing" \
--location-notes ""
Peer lookup tassonomia (eseguilo ogni 5-10 record)
python3 /app/shared/skills/db_query.py raw \
"SELECT role_family, COUNT(*) AS n
FROM positions WHERE role_family IS NOT NULL
GROUP BY role_family ORDER BY n DESC"
Promozione a checked (SOLO dopo enrichment completo)
python3 /app/shared/skills/db_update.py position <ID> --status checked \
--notes "ESPERIENZA: ... \\n LINGUA: ... \\n SENIORITY: ..."