fix(runner): arresta il servizio su config JSON invalida (no piu' loop muto) #1

Merged
claudia merged 3 commits from fix/runner-fatal-on-bad-config into main 2026-06-17 16:56:47 +00:00
Owner

Problema

Aggiungendo un progetto con un errore di sintassi nel config.json, il runner partiva senza segnalare nulla ma non caricava progetti.

La causa non era il parsing (config.Load falliva gia' correttamente), ma due buchi a valle:

  1. In main.go, config.Load gira prima dell'init di runlog, quindi il log.Fatalf finiva solo su stderr → invisibile sotto il servizio Windows (niente console).
  2. Il launcher trattava l'exit code≠0 come crash qualsiasi → restart loop infinito. Una config rotta non si ripara riavviando: servizio "su", worker morto in loop.

Fix

  • internal/exitcode (nuovo): codici convenzionali worker→launcher — 75 UpdateReady, 78 FatalConfig (EX_CONFIG).
  • Worker: su config invalida logga in modo duraturo su runner.log (oltre a stderr) ed esce con 78.
  • Launcher: riconosce 78 come fatale non-recuperabile → logga e arresta il servizio. Inserito prima del check canary, cosi' un config error non viene scambiato per un binario difettoso da rollare.
  • config.DefaultLogDir esportata (riuso in resolveLogDir + worker).

Test

  • internal/config: regressione su JSON malformati (leading-zero 0000, virgola di troppo, chiave senza virgola) e campi obbligatori mancanti.
  • cmd/launcher: i due exit-path (arresto su 78, supervisione su crash generico).
  • Suite completa verde, go vet e gofmt puliti, build linux + cross-Windows OK.

Note

  • dist/config.json ha ancora il placeholder "project_id": 0000 (invalido): col fix ora il runner lo rifiuta esplicitamente invece di morire muto.
  • version.Runner non bumpato (resta 0.6.4): da fare al rilascio se serve.

🤖 Generated with Claude Code

## Problema Aggiungendo un progetto con un errore di sintassi nel `config.json`, il runner *partiva senza segnalare nulla ma non caricava progetti*. La causa **non** era il parsing (`config.Load` falliva gia' correttamente), ma due buchi a valle: 1. In `main.go`, `config.Load` gira **prima** dell'init di `runlog`, quindi il `log.Fatalf` finiva **solo su stderr** → invisibile sotto il servizio Windows (niente console). 2. Il **launcher** trattava l'exit code≠0 come crash qualsiasi → **restart loop infinito**. Una config rotta non si ripara riavviando: servizio "su", worker morto in loop. ## Fix - **`internal/exitcode`** (nuovo): codici convenzionali worker→launcher — `75` UpdateReady, **`78` FatalConfig** (EX_CONFIG). - **Worker**: su config invalida logga in modo **duraturo** su `runner.log` (oltre a stderr) ed esce con `78`. - **Launcher**: riconosce `78` come fatale non-recuperabile → logga e **arresta il servizio**. Inserito **prima** del check canary, cosi' un config error non viene scambiato per un binario difettoso da rollare. - `config.DefaultLogDir` esportata (riuso in `resolveLogDir` + worker). ## Test - `internal/config`: regressione su JSON malformati (leading-zero `0000`, virgola di troppo, chiave senza virgola) e campi obbligatori mancanti. - `cmd/launcher`: i due exit-path (arresto su `78`, supervisione su crash generico). - Suite completa verde, `go vet` e `gofmt` puliti, build linux + cross-Windows OK. ## Note - `dist/config.json` ha ancora il placeholder `"project_id": 0000` (invalido): col fix ora il runner lo **rifiuta esplicitamente** invece di morire muto. - `version.Runner` non bumpato (resta 0.6.4): da fare al rilascio se serve. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Il worker, su config illeggibile/malformata, falliva ma:
- il log.Fatalf finiva solo su stderr (invisibile sotto il servizio Windows)
  perche' girava prima dell'init di runlog -> "non segnalava nulla";
- il launcher trattava l'exit come crash qualsiasi e riavviava in loop
  infinito -> servizio "su" ma nessun progetto caricato.

Fix:
- internal/exitcode: codici convenzionali worker->launcher
  (75 UpdateReady, 78 FatalConfig = EX_CONFIG).
- worker: su errore di config logga in modo duraturo su runner.log (oltre a
  stderr) ed esce con 78.
- launcher: riconosce 78 come fatale non recuperabile -> logga e ARRESTA il
  servizio (prima del check canary, cosi' non scambia un config error per un
  binario difettoso da rollare).
- config.DefaultLogDir esportata (riuso in resolveLogDir + worker).

Test: regressione su JSON malformati/campi mancanti (internal/config) e sui
due exit-path del launcher (arresto su 78, supervisione su crash generico).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Include il fix 'arresto su config JSON invalida' (questa branch). Release
runner-v0.6.5 da pubblicare come latest nel gateway (GW_RUNNER_LATEST_*).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Il runner cadeva con "cannot unmarshal string into Inbound.ticket_id of type
int" quando un client (es. i miei script .mjs del progetto sito) manda ticket_id
come slug stringa ("site-webp-001"). Un singolo frame con ticket_id stringa
buttava giu' l'intera connessione del runner -> retry-loop su tutti i progetti.

- protocol: nuovo tipo TicketID (string) con UnmarshalJSON che accetta intero
  JSON, stringa, o null/assente. Inbound.TicketID e TaskStartedFrame adeguati.
- runner: ticket_id ora stringa end-to-end (sessionState, runlog, nomi file
  transcript, attachmentsDirFor con sanitize, primo prompt). Retrocompat: un
  ticket_id intero (gw.py / ]po[) diventa "7777", invariato nei path/log.
- version 0.6.5 -> 0.6.6 (la 0.6.6 include ANCHE il fix arresto-su-config-invalida).

Test: nuovo protocol_test (int/stringa/slug/null) + adeguati runlog/helpers test.
go vet/gofmt puliti, cross-build win/amd64.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
claudia deleted branch fix/runner-fatal-on-bad-config 2026-06-17 16:56:47 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Soltea-org/soltea-agent-gateway!1
No description provided.