# CLAUDE.md — gate-ufficio **Versione:** 0.11 **Ultimo aggiornamento:** 06/09/2026 Eredita la Costituzione principale (`Dropbox/adrian/CLAUDE.md`) — questo file contiene solo contesto locale. Storia/motivazione delle decisioni: solo nei log commit git di questa cartella (convenzione dichiarata in `archivio/Mauro/lavoro/lavoro-python-ecosistema/schedulatore-job-status.md`, non duplicata qui). ## Cosa è Esecutore muto lato ufficio che sostituisce [[Frank]] — nessuna GUI, nessuna logica di scheduling/decisione propria, **zero LLM nel ciclo**: `scheduler_locale.py` legge `nave/schedule.json` ogni 60s, per ogni job dovuto lancia `run_job.py` come processo OS a sé (`subprocess.Popen`, fire-and-forget), che esegue il `.bat` e chiude il log con una riga `=== ESITO: ... ===`. Il "cervello" (quando lanciare cosa) vive su Nave in `schedule.json`, sotto git. **Stato attuale (job in produzione, retention backup, salute)**: `archivio/Mauro/lavoro/ lavoro-python-ecosistema/schedulatore-job-status.md` — unica fonte, non duplicare qui. **Nessuna modifica reale a `PYTHON/Frank/` senza supervisione diretta di Mauro** (Costituzione) — comunque non più rilevante in pratica: `gate-ufficio` non tocca né importa nulla di Frank. ## Struttura `ufficio/` gira sul PC ufficio, `nave/` decide/osserva da Nave — separazione fisica per evitare di mescolare codice delle due macchine nella stessa cartella. **`ufficio/`:** - `scheduler_locale.py` — loop principale, un solo processo, PID in `scheduler.pid` (prova di salute più affidabile del loop, non il log) - `run_job.py` — esecuzione isolata di un job: apre il log, `cmd.exe /c ""`, `process.wait(timeout=...)`, kill-process-tree via `psutil` su timeout - `jobs/` — l'allowlist stessa: un `.bat` per job (nome = stem del filename), `.timeout` opzionale accanto (default `DEFAULT_TIMEOUT_SECONDS=3600`) - `run_scheduler.bat` — unico punto d'ingresso (doppio click), self-healing venv, consegna a `run_scheduler_hidden.vbs` → `run_scheduler_background.bat` (finestra nascosta) - `stop_scheduler.bat` — legge il PID da `scheduler.pid`, `taskkill /F` - `requirements.txt`; `bin/` (non versionato, es. `restic.exe`); `secrets/` (non versionato, es. `restic_password` — mai committare, mai in chiaro nei `.bat`) **`nave/`:** - `schedule.json` — `{job_name: {cron, command, timeout_seconds}}`, fonte di verità, sotto git - `monitor_executions.py` — segue `../ufficio/logs/*.log` in tempo (quasi) reale, usato dal Monitor armato in `avvio-sessione` - `check_job_health.py` — legge `schedule.json`, segnala job in ritardo oltre tolleranza **Demo:** `demo/run_scheduler_locale_demo.py` — `is_due()`, dispatch selettivo `--once`, casi successo/fallimento. ## Entry point ``` python -m venv .venv .venv/Scripts/pip install -r ufficio/requirements.txt # o .venv/bin/pip su Nave/Linux .venv/Scripts/python ufficio/scheduler_locale.py # loop reale .venv/Scripts/python ufficio/scheduler_locale.py --once # un solo ciclo, per test .venv/Scripts/python demo/run_scheduler_locale_demo.py ``` `run_scheduler.bat` resta il modo giusto per avviare a mano (self-healing del venv incluso). Nessun autostart di sistema (Task Scheduler rimosso attivamente da Direzione Sistemi, scartato come opzione) — avvio/verifica è compito di `gemello-ufficio` all'apertura della giornata. ## Gotcha ancora validi - **Mai creare/cancellare `.venv` qui da Nave** — si propaga via Dropbox come cancellazione anche verso Ufficio (stessa cartella per entrambe le macchine), ha già rotto il venv Windows in uso una volta. Se serve un venv Linux per test, fuori da `Dropbox/adrian/`. Il venv Windows si autoripara da solo se rotto (`run.bat`/`run_scheduler.bat` ricreano `Scripts\python.exe` se manca). - **"Processi duplicati" in Task Manager è un falso allarme**: ogni processo Python via venv mostra due righe (stub `.venv\Scripts\python(w).exe` + interprete reale Python311), stessa command line — non sono due istanze. Contare le istanze reali dividendo le righe per 2, mai uccidere una riga della coppia pensando di "correggere un duplicato". - **Non riparte da solo dopo un riavvio non pulito**: `scheduler.pid` può sopravvivere con un PID poi riassegnato a un processo qualunque — il controllo "già in esecuzione?" lo legge, lo trova vivo (anche se è un altro processo) e blocca un nuovo avvio senza errore visibile. Fix: cancellare il file di lock stantio prima di riprovare. - **Log a 0 byte appena dopo l'avvio non è un errore** — stdout bufferizzato a blocchi quando rediretto su file (`run_scheduler_background.bat`).