com um clique
salary-estimate
Stima salariale gerarchica per il Scorer (bug
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Menu
Stima salariale gerarchica per il Scorer (bug
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Extract the company logo for a company in the companies table and store it as a small base64 data-URI (max ~35KB, min 32px). Primary path is fully automated via logo_fetch.py against the official website (apple-touch-icon → icon → og:image → favicon); when the site blocks bots or has no usable icon, find a direct logo image URL via web search and pass it with --from-url. Verify the website really belongs to the company BEFORE fetching. Sets companies.logo, logo_source, logo_fetched.
Lo sweep di manutenzione INFRA del Mantenitore 👷♂️ (gemello del Dottore, scope infrastruttura non agenti). Una passata giornaliera one-shot: canary di liveness dei processi salva-vita del container (bridge/daemon/watchdog) via process_health.py, smoke-test dei tool mission-critical (browser/LinkedIn) via tool_health.py, audit/consolidamento deps fuori standard, GC di script e tmp orfani, de-dup di script ricorrenti, freschezza deps, trend disco/RAM. Single-writer: il Mantenitore è l'UNICO che ripara l'infra; le azioni DISTRUTTIVE (delete/archive) le PROPONE, il Capitano decide. Esito in append su mantenitore-logbook.jsonl.
Doctor-only. Context-refresh round: for each agent session read its real context occupancy (provider client-side command, zero tokens) and refresh ONLY sessions whose context window is >50% full — do a retrospective (capture + interview + analytics), append a dense synthesis to the growing daily journal, then KILL + recreate + resume the session with continuation context, so its context window is cleared without losing where it was. Runs 2× per work window (at +30min and at mid). Skips fresh, low-context (≤50%), and Capitano-parked sessions.
Run the mandatory 3-round CV review loop with the Critico — autonomously, without going through the Capitano. For each round you spawn a FRESH `CRITICO-S<N>` session (same N as your Scrittore session: SCRITTORE-2 → CRITICO-S2), send PDF + JD, wait for the structured verdict, kill the Critic, correct the CV, regenerate the PDF, and start the next round with another fresh instance. Three rounds are non-negotiable — neither 1 nor 2. After the 3rd round, gate: `critic_score ≥ 5` → `ready`, else `excluded`. Owned by the Scrittore.
DB + filesystem contract every Scrittore follows when taking a position from `scored` (≥50) to `ready`/`excluded`. Three gates BEFORE writing a single line of CV (anti-rewriting, anti-collision, link verification), one canonical path for deliverables, one final gate after the 3rd Critic round. Skipping any of these produces duplicate work, overwrites another Writer's claim, or — worst — pushes an `excluded`-grade CV to the user as `ready`. Owned by the Scrittore.
Insert NEW records into the JHT DB (positions / scores / applications / companies / position_highlights). Use it ONLY when an agent needs to create a record — Scout for positions, Analyst for companies and highlights, Scorer for scores, Writer for applications. Never blind-overwrite — for updates use `db-update`.
Baseado na classificação ocupacional SOC
| name | salary-estimate |
| description | Stima salariale gerarchica per il Scorer (bug |
| allowed-tools | Bash(python3 /app/shared/skills/salary_estimate.py *), Bash(python3 /app/shared/skills/db_update.py *) |
Snapshot 2026-05-17 (43 score Kimi): 41 score su 43 con
salary_fit=5/10 (default "no data no bias"), 2 con valori reali da
JD esplicito. Risultato: salary_fit (peso 10/100) era de facto
inerte — spazio decisionale dello Scorer ridotto da 100 a 95.
Causa: nessuno popolava salary_estimated_*. Lo Scorer è onesto,
non inventa, e senza dato ricade sul default. Decisione utente:
costruire una cache locale degli stimati così la prima fetch costa, le
successive sono gratis. "I salari non cambiano di settimana in
settimana, ma di anno in anno".
Se positions.salary_declared_min e salary_declared_max non NULL →
usa quelli, niente stima. Lo Scrittore può chiamare:
python3 /app/shared/skills/salary_estimate.py --position-id 42
Lo script legge i declared dalla DB e ritorna level=1 con i numeri.
Path: /jht_home/.cache/salary_estimates.json. Key:
(stack, seniority, country, mode). TTL 30 giorni.
python3 /app/shared/skills/salary_estimate.py \
--stack python --seniority junior --country IT --mode remote
Hit → JSON con level=2, source=cache, min, max. Miss → cade su L3
o L4.
Per ora ritorna None: la skill cade direttamente su L4. Quando F-2 (Scout web access) sarà disponibile, lo Scout/Analista popolerà la cache via web search Glassdoor/Levels/Indeed. Da quel momento il primo lookup di una nuova combinazione fa una sola fetch, poi 29 giorni di hit gratuiti.
Se tutti i livelli sopra falliscono → ritorna level=4, min=null, max=null, estimation_failed=true, reason="no_data_default". Lo Scorer
mette salary_fit=5 E aggiunge no_data_default in score.notes —
così il Mentor (downstream) non propaga il 5 come dato reale ma come
"N/D" (vedi bug #27 fix Mentor).
{
"level": 1 | 2 | 3 | 4,
"min": int | null,
"max": int | null,
"currency": "EUR",
"source": "declared" | "cache" | "web" | "default",
"fetched_at": "YYYY-MM-DD",
"estimation_failed": false | true,
"reason": "<optional>"
}
result=$(python3 /app/shared/skills/salary_estimate.py \
--stack "$STACK" --seniority "$SENIORITY" \
--country "$COUNTRY" --mode "$MODE" \
--declared-min "$DECL_MIN" --declared-max "$DECL_MAX")
# 1. Estrai i campi
min=$(echo "$result" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['min'] or '')")
max=$(echo "$result" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['max'] or '')")
failed=$(echo "$result" | python3 -c "import sys,json; print(json.load(sys.stdin).get('estimation_failed', False))")
# 2. Se ha numeri reali, popola positions.salary_estimated_*
if [ -n "$min" ] && [ -n "$max" ]; then
python3 /app/shared/skills/db_update.py position "$POS_ID" \
--salary-estimated-min "$min" --salary-estimated-max "$max" \
--salary-estimated-source "salary-estimate"
fi
# 3. Calcola salary_fit (0-10) con la tua logica esistente
# (confronto con target candidato da candidate_profile.salary_annual_eur)
# e include la nota "no_data_default" se failed=True.
Per scaldare la cache su un nuovo container (es. test):
python3 /app/shared/skills/salary_estimate.py --seed-cache \
--stack python --seniority junior --country IT --mode remote \
--declared-min 28000 --declared-max 38000
In produzione la cache si scalda da sola: L1 (declared dal JD) + futuro L3 (web search) la popolano organicamente nell'arco di una settimana di operatività.
python junior IT remote rieseguito 10 volte =
9 fetch sprecate.agents/_skills/db-update/SKILL.md § Positions — salary-estimated-*docs/examples/candidate_profile.yml.example — salary_annual_eur (target candidato,
side-fix bug #27)agents/_skills/mentor-output/SKILL.md — hide "5 passivo" quando
notes contiene no_data_default