ワンクリックで
salary-estimate
Stima salariale gerarchica per il Scorer (bug
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Stima salariale gerarchica per il Scorer (bug
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
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`.
| 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