# Piano dei test Decisioni (strumenti, livelli, struttura): [ADR-0003](adr/0003-strategia-di-test.md). Qui: regola, flusso, soglie e **matrice requisito → test**, sorgente di verità per la copertura. Le spunte `[ ]` passano a `[x]` quando il test esiste e passa. ## Regola **Nuova funzione, correzione o cambio di comportamento ⇒ test nello stesso commit.** Niente test, funzione non finita. - Una nuova funzione aggiunge righe alla matrice qui sotto (con ID) prima di scrivere il codice. - Una correzione di bug parte da un test che fallisce e lo lascia come regressione. - Una modifica che cambia un requisito aggiorna prima il doc, poi la riga della matrice, poi i test, poi il codice. - Una riga senza test non è copertura: va lasciata `[ ]` e conta come debito visibile. ## Flusso (estende il doc-first di [README.md](README.md)) ADR (se serve) → MVP.md (requisito e criterio) → riga in questa matrice → test che falliscono → codice → test verdi → ROADMAP/README/AGENT.md. ## Livelli | Livello | Cosa prova | Come | |---|---|---| | **U** — unitario | `Core/` e `MainViewModel` | xUnit, finto runner, cartella dati temporanea | | **R** — renderer | `MarkdownView` | xUnit + Headless, asserzioni sull'albero visuale | | **G** — GUI headless | `MainWindow`, template, tastiera, finestre di dialogo | xUnit + Headless, senza schermo | | **P** — processo | avvio reale di un processo (`Claude`) | `FakeClaude` nel PATH | | **E** — end-to-end | l'eseguibile pubblicato (CLI, 127, exit code) | publish + processo figlio + `FakeClaude` | ## Struttura ``` agent-wiki-ui.slnx src/agent-wiki-ui/ app (Core/, Gui/, Program.cs) tests/agent-wiki-ui.Tests/ Support/ (TestApp, PngFactory), Gui/ (R, G), Process/ (P), Core/ (U, quando arrivano), E2E/ (E) tests/FakeClaude/ eseguibile finto `claude` (vedi sotto) ``` Una cartella per livello, creata quando c'è il primo test; il livello si dichiara anche con `[Trait("Category", "Unit" | "Gui" | "Process" | "E2E")]`. ### FakeClaude Piccolo eseguibile (`AssemblyName` `claude`, nel PATH dei test) che prende il posto del vero motore. Un test lo configura con variabili d'ambiente del processo che avvia: | Variabile | Effetto | |---|---| | `FAKE_CLAUDE_LOG` | file in cui accoda una riga JSON per invocazione: `{"args": [...], "cwd": "...", "stdin": "..."}` | | `FAKE_CLAUDE_OUT` | testo scritto su stdout (anche per `--version`) | | `FAKE_CLAUDE_ERR` | testo scritto su stderr | | `FAKE_CLAUDE_EXIT` | exit code (predefinito 0) | | `FAKE_CLAUDE_DELAY_MS` | attesa prima di rispondere | Legge stdin fino alla fine e lo registra: se il chiamante non lo chiude, il processo non termina (così CL-05 è verificabile). Il percorso del file compilato lo fornisce l'helper di test `FakeClaudePath`. ## Predisposizioni di testabilità (prerequisito dei test) - [ ] `ChatStore` come istanza con cartella iniettata (oggi statico su `LocalApplicationData`) - [ ] `IEngineRunner` dietro `Core/EngineRunner.cs`, iniettato nel `MainViewModel` - [x] `EngineUpdater.CheckAsync` accetta un `HttpClient`: i test usano un gestore finto (rete assente, risposta non valida, versioni di prova) - [x] `InternalsVisibleTo` verso il progetto di test - [x] Spostamento in `src/agent-wiki-ui/` e `tests/` con soluzione unica `agent-wiki-ui.slnx` ## Soglie di copertura (la build fallisce sotto soglia) | Area | Righe | Rami | |---|---|---| | `Core/` | ≥ 95% | ≥ 90% | | `Gui/MainViewModel` | ≥ 95% | ≥ 90% | | `Gui/MarkdownView` | ≥ 90% | ≥ 85% | | `Gui/MainWindow*`, `ConfirmDialog` (code-behind) | ≥ 85% | — | | Totale | ≥ 90% | — | Esclusi dal calcolo: XAML generato, `Program.cs` (tre righe, coperte da E2E), `App.axaml.cs`. La soglia non sostituisce la matrice: copertura di righe alta con requisiti scoperti non basta. ## Matrice requisito → test Fonte dei requisiti: [MVP.md](MVP.md) e gli ADR. Livello: vedi sopra. Il nome del test è indicativo. ### Avvio dei motori — `Core/EngineRunner.cs` | ID | Requisito | Liv. | Test | | |---|---|---|---|---| | CL-01 | Il prompt è un solo argomento, anche con virgolette e spazi (criterio MVP 4) | U/P | `Prompt_con_virgolette_arriva_intatto` | [ ] | | CL-02 | Primo messaggio: `--session-id `; successivi: `--resume ` | U | `Sessione_prima_e_ripresa` | [ ] | | CL-03 | `--model`/`--effort` omessi se vuoti, presenti se valorizzati | U | `Flag_opzionali` | [ ] | | CL-04 | Cartella di lavoro = cartella dell'exe, non la cwd del chiamante (criterio MVP 1) | U/P | `Cartella_di_lavoro_e_quella_dell_exe` | [ ] | | CL-05 | Stdin chiuso subito: nessuna attesa di 3 s | P | `Stdin_chiuso_non_blocca` | [ ] | | CL-06 | CLI: l'exit code di `claude` è propagato (criterio MVP 3) | P/E | `ExitCode_propagato` | [ ] | | CL-07 | CLI: eseguibile del motore assente ⇒ messaggio chiaro (con il nome dell'eseguibile) ed exit code 127 | P/E | `Claude_assente_127` | [ ] | | CL-08 | CLI: stdout/stderr ereditati e ritrasmessi (criterio MVP 2: streaming) | E | `Output_ereditato_in_tempo_reale` | [ ] | | CL-09 | GUI: output catturato e ripulito agli estremi; stderr separato | P | `Output_catturato` | [ ] | | CL-10 | GUI: `claude` assente ⇒ eccezione riconoscibile, non crash | P | `Claude_assente_in_GUI` | [ ] | | CL-11 | Il catalogo modelli contiene gli ID attesi e gli effort previsti | U | `Catalogo_modelli_ed_effort` | [ ] | | CL-12 | Senza argomenti non parte la CLI; con argomenti sì (tutti uniti in un prompt) | E | `Argomenti_uniti_in_un_prompt` | [ ] | ### Codex — `Core/Engine.cs` (parser e argomenti), `Core/EngineRunner.cs` Le righe CL valgono per ogni motore; queste sono specifiche di Codex (ADR-0002). Il parser si prova con output JSONL reali salvati nei test. | ID | Requisito | Liv. | Test | | |---|---|---|---|---| | CX-01 | Primo messaggio: `exec --json --skip-git-repo-check -s read-only … -- ` | U | `Codex_primo_messaggio` | [ ] | | CX-02 | Ripresa: `exec resume --json … -c sandbox_mode="read-only" -- `, senza `-s` | U | `Codex_ripresa` | [ ] | | CX-03 | Un prompt che inizia con `-` arriva intatto (grazie a `--`) | U/P | `Codex_prompt_con_trattino` | [ ] | | CX-04 | Modello ⇒ `-m`; effort ⇒ `-c model_reasoning_effort=`; vuoti ⇒ omessi | U | `Codex_modello_ed_effort` | [ ] | | CX-05 | Il parser legge `thread_id` da `thread.started` | U | `Codex_thread_id` | [ ] | | CX-06 | La risposta è l'ultimo `agent_message`, anche con più messaggi | U | `Codex_ultimo_agent_message` | [ ] | | CX-07 | `turn.failed`: messaggio interno di `error.message` estratto dal JSON annidato; exit ≠ 0 ⇒ errore | U | `Codex_errore_turno` | [ ] | | CX-08 | Eventi `item.completed` di tipo `error` (avvisi) non fanno fallire la risposta | U | `Codex_avvisi_ignorati` | [ ] | | CX-09 | Exit 0 senza `agent_message` ⇒ errore "nessuna risposta" | U | `Codex_nessuna_risposta` | [ ] | | CX-10 | Righe non JSON o eventi sconosciuti sono ignorati senza eccezioni | U | `Codex_righe_non_json` | [ ] | | CX-11 | In modalità CLI (output ereditato) non si usa `--json` | U | `Codex_cli_senza_json` | [ ] | | CX-12 | Con un `FakeClaude` configurato come Codex il ciclo primo messaggio + ripresa funziona (id letto e riusato) | P | `Codex_ciclo_completo_con_finto` | [ ] | ### Aggiornamento dei CLI — `Core/EngineUpdater.cs`, `Gui/MainViewModel.cs` (ADR-0004) Si provano con `FakeClaude`/`FakeCodex` che rispondono a `--version` e `update`; nessun test aggiorna un CLI reale. | ID | Requisito | Liv. | Test | | |---|---|---|---|---| | UP-01 | Versione letta da `--version` (prima riga ripulita); eseguibile assente ⇒ versione nulla | P | `Versione_letta` | [ ] | | UP-02 | Versione cambiata dopo `update` ⇒ esito "Aggiornato da X a Y" | P | `Aggiornato` | [ ] | | UP-03 | Exit 0 e versione invariata ⇒ "Già aggiornato (X)" | P | `Gia_aggiornato` | [ ] | | UP-04 | Exit ≠ 0 ⇒ errore con le ultime righe dell'output e il comando manuale | P | `Aggiornamento_fallito` | [ ] | | UP-05 | Tempo massimo superato ⇒ processo terminato ed errore chiaro | P | `Aggiornamento_scaduto` | [ ] | | UP-06 | Eseguibile assente ⇒ riga non offerta / errore "non trovato" | U | `Aggiornamento_senza_eseguibile` | [ ] | | UP-07 | Metodo di installazione dedotto dal percorso reale (npm, Homebrew, WinGet, nativo) con collegamenti simbolici risolti | U | `Metodo_di_installazione` | [ ] | | UP-08 | Comando manuale corretto per ogni coppia motore × metodo (tabella dell'ADR-0004) | U | `Comando_manuale` | [ ] | | UP-09 | Un solo aggiornamento alla volta; durante una risposta in corso i pulsanti sono disabilitati e la chat non parte durante l'aggiornamento | U | `Aggiornamento_esclusivo` | [ ] | | UP-10 | Dopo un aggiornamento riuscito le versioni mostrate si rileggono; la chat aperta resta invariata | U | `Versioni_rilette` | [ ] | | UP-11 | Le righe di aggiornamento compaiono solo per i CLI installati | U/G | `Righe_solo_installati` | [ ] | | UP-12 | Il pulsante Aggiorna del riquadro agisce solo sul CLI della sua riga | G | `Pulsante_per_riga` | [ ] | | UP-13 | Nessun aggiornamento parte senza un clic | U | `Nessun_aggiornamento_automatico` | [ ] | | UP-14 | Claude Code: l'ultima versione si legge dal testo di `…/claude-code-releases/latest` (spazi e a capo ripuliti) | U | `Ultima_versione_claude` | [ ] | | UP-15 | Codex: l'ultima versione si legge da `tag_name` della release GitHub (`rust-v0.162.1` ⇒ 0.162.1); intestazione User-Agent inviata | U | `Ultima_versione_codex` | [ ] | | UP-16 | Confronto: ultima > installata ⇒ disponibile; uguale o più nuova ⇒ aggiornato; versioni con testo attorno (`2.1.300 (Claude Code)`, `codex-cli 0.162.1`) lette bene | U | `Confronto_versioni` | [ ] | | UP-17 | Rete assente, risposta non valida o timeout ⇒ stato "sconosciuto", nessuna eccezione, pulsante normale | U | `Controllo_senza_rete` | [ ] | | UP-18 | Disponibile ⇒ riga con "Aggiornamento disponibile: X", pulsante evidenziato e puntino sul pulsante Impostazioni; aggiornato ⇒ nessuno dei tre | U/G | `Evidenziazione_aggiornamento` | [ ] | | UP-19 | Il controllo parte all'avvio e all'apertura del riquadro solo se l'ultimo ha più di 5 minuti; dopo un aggiornamento riuscito si ripete; non blocca invio né interfaccia | U | `Quando_si_controlla` | [ ] | | UP-20 | Il controllo non installa nulla e non modifica i CLI | P | `Controllo_in_sola_lettura` | [ ] | ### Cronologia — `Core/ChatStore.cs`, `Core/AppInfo.cs` | ID | Requisito | Liv. | Test | | |---|---|---|---|---| | ST-01 | Salvataggio e rilettura: id, titolo, data, `started`, messaggi con ruolo e testo | U | `Salva_e_rileggi` | [ ] | | ST-02 | Una chat senza messaggi non viene salvata | U | `Chat_vuota_non_salvata` | [ ] | | ST-03 | Titolo = inizio del primo messaggio utente: 50 caratteri + "…", a capo sostituiti da spazi | U | `Titolo_troncato` | [ ] | | ST-04 | L'id deve essere un Guid: un id con `..` o separatori non tocca il disco fuori dalla cartella | U | `Id_non_guid_rifiutato` | [ ] | | ST-05 | L'elenco è ordinato dalla più recente | U | `Elenco_ordinato` | [ ] | | ST-06 | File JSON corrotto ignorato senza eccezioni | U | `File_corrotto_ignorato` | [ ] | | ST-07 | Eliminazione: rimuove solo il file di quella chat; id inesistente senza errori | U | `Elimina_chat` | [ ] | | ST-08 | Compatibilità del formato: un JSON scritto dalla versione WinForms si legge | U | `Formato_storico_compatibile` | [ ] | | ST-09 | Migrazione: la cartella di un nome precedente (`LlmRun`) si sposta nella nuova al primo avvio; se la nuova esiste non si tocca nulla | U | `Migrazione_da_nome_precedente` | [ ] | | ST-10 | `AppInfo.Name` = `AssemblyName` del csproj | U | `Nome_da_assembly` | [ ] | | ST-12 | Il JSON salva `Engine` e `SessionId`; un file senza questi campi si legge come chat di Claude Code con `SessionId` = id | U | `Motore_e_sessione_salvati_e_compatibilita` | [ ] | | ST-11 | Errore di I/O nel salvataggio non perde la chat a schermo | U | vedi VM-12 | [ ] | ### Logica della chat — `Gui/MainViewModel.cs` | ID | Requisito | Liv. | Test | | |---|---|---|---|---| | VM-01 | Senza chat salvate: schermata iniziale (`ShowEmpty`), elenco vuoto | U | `Avvio_senza_chat` | [ ] | | VM-02 | All'avvio si riapre la chat più recente | U | `Avvio_riapre_ultima` | [ ] | | VM-03 | `CanSend` falso se il testo è vuoto o solo spazi, o se occupato | U | `CanSend` | [ ] | | VM-04 | Invio: il messaggio utente compare e viene **salvato prima** della risposta | U | `Messaggio_salvato_prima_della_risposta` | [ ] | | VM-05 | Risposta riuscita: aggiunta come "ai", `started` diventa vero, il secondo invio usa `--resume` | U | `Risposta_e_ripresa` | [ ] | | VM-06 | Risposta fallita (exit ≠ 0): messaggio "err" con codice e stderr; `started` resta falso e il tentativo successivo usa ancora `--session-id` | U | `Errore_non_avvia_la_sessione` | [ ] | | VM-07 | `claude` assente: messaggio "'claude' non trovato nel PATH." | U | `Claude_assente_messaggio` | [ ] | | VM-08 | Durante l'attesa: Invia e Nuova chat disabilitati; aprire/eliminare è ignorato | U | `Occupato_blocca_azioni` | [ ] | | VM-09 | Modello: "Predefinito" ⇒ nessun flag; modello scelto ⇒ ID; "Altro ID…" ⇒ testo libero ripulito | U | `Scelta_modello` | [ ] | | VM-10 | Effort: "Predefinito" ⇒ nessun flag; altrimenti il valore | U | `Scelta_effort` | [ ] | | VM-20 | Valori iniziali: modello più piccolo del motore (Claude Code: Haiku 5.5; Codex: GPT-5.6 Luna) ed effort `low`; il primo invio passa `--model`/`--effort` (o `-m`/`-c model_reasoning_effort`) con quei valori | U | `Valori_iniziali` | [ ] | | VM-11 | Nuova chat: svuota la vista, nuovo guid, `started` falso, elenco aggiornato | U | `Nuova_chat` | [ ] | | VM-12 | Errore di I/O nel salvataggio non interrompe l'invio né cancella i messaggi | U | `Salvataggio_fallito_non_perde_chat` | [ ] | | VM-13 | Apertura chat: carica messaggi, `started` e id; chat inesistente ⇒ elenco ricaricato | U | `Apri_chat` | [ ] | | VM-14 | Eliminazione: con conferma rimuove; senza conferma non fa nulla; se è la chat aperta parte una nuova | U | `Elimina_con_e_senza_conferma` | [ ] | | VM-16 | Barra laterale: il comando commuta `SidebarOpen` | U | `Barra_laterale` | [ ] | | VM-17 | Copia: il comando passa il testo alla funzione degli appunti | U | `Copia_negli_appunti` | [ ] | | VM-18 | Notifiche di proprietà: `HasMessages`, `ShowEmpty`, `CanSend`, `IsCustomModel` cambiano quando devono | U | `Notifiche_proprieta` | [ ] | | VM-19 | Il titolo e la data dell'elenco riflettono l'ultimo scambio; la chat aperta è marcata attiva | U | `Elenco_chat_attiva` | [ ] | ### Renderer markdown — `Gui/MarkdownView.cs` | ID | Requisito | Liv. | Test | | |---|---|---|---|---| | MD-01 | Sorgente vuota o nulla ⇒ nessun contenuto, senza errori | R | `Sorgente_vuota` | [ ] | | MD-02 | Titoli h1–h6 con dimensioni decrescenti e peso semi-grassetto | R | `Titoli` | [ ] | | MD-03 | Paragrafi; a capo morbido = spazio, a capo forzato = riga nuova | R | `A_capo` | [ ] | | MD-04 | Grassetto, corsivo, barrato, anche annidati | R | `Enfasi` | [ ] | | MD-05 | Codice inline in carattere monospazio con sfondo | R | `Codice_inline` | [ ] | | MD-06 | Blocco di codice: monospazio, scorrimento orizzontale, pulsante Copia che copia solo il codice | R/G | `Blocco_codice_e_copia` | [ ] | | MD-07 | Liste puntate e numerate (rispetta il numero iniziale), annidate | R | `Liste` | [ ] | | MD-08 | Citazioni con barra laterale, anche annidate | R | `Citazioni` | [ ] | | MD-09 | Tabelle: intestazione in evidenza, allineamenti sinistra/centro/destra, righe irregolari | R | `Tabelle` | [ ] | | MD-10 | Linea orizzontale | R | `Linea_orizzontale` | [ ] | | MD-11 | Link: colore d'accento e sottolineatura; il clic apre solo `http(s)` | R | `Link_http_https` | [ ] | | MD-12 | Link: nessuna apertura se c'è una selezione in corso; cursore a mano sopra il link | R | `Link_con_selezione` | [ ] | | MD-13 | Posizioni dei link corrette con enfasi, codice inline e a capo prima del link | R | `Intervalli_link` | [ ] | | MD-14 | Immagini con URL `http(s)` o altro schema: si mostra il testo alternativo, nessuna richiesta di rete | R | `Immagini_remote_come_testo` | [x] | | MD-19 | Immagine con percorso assoluto di un file png valido ⇒ controllo immagine con le dimensioni attese (ADR-0006) | R | `Immagine_percorso_assoluto` | [x] | | MD-20 | Percorso relativo risolto rispetto a `AppContext.BaseDirectory`; anche URI `file://` | R | `Immagine_percorso_relativo_e_file_uri` | [x] | | MD-21 | File mancante, illeggibile, corrotto o con estensione non ammessa ⇒ testo alternativo, nessuna eccezione | R | `Immagine_non_valida_ripiego` | [x] | | MD-22 | File oltre il limite di 20 MB ⇒ testo alternativo, file non decodificato | R | `Immagine_oltre_limite` | [x] | | MD-23 | Immagine più larga della colonna ⇒ ridotta in larghezza mantenendo le proporzioni; mai ingrandita oltre l'originale | R | `Immagine_larghezza_massima` | [x] | | MD-24 | Il testo alternativo è nome accessibile e suggerimento dell'immagine | R | `Immagine_testo_alternativo_accessibile` | [x] | | MD-25 | Immagine dentro un link, in una lista o in una tabella ⇒ resa senza rompere intervalli dei link (MD-13) né il layout | R | `Immagine_in_contesti_annidati` | [x] | | MD-26 | Cambio di tema con immagini presenti ⇒ vista ricostruita, immagini ancora mostrate | R | `Immagine_cambio_tema` | [x] | | MD-27 | Il caricamento è in background: subito dopo il rendering c'è il segnaposto `[alt]` e un'immagine in attesa; a fine decodifica compare l'immagine; un file corrotto lascia `[alt]` | R | `Immagine_caricamento_in_background` | [x] | | MD-28 | Percorso relativo non presente nella cartella dell'exe ma in una cartella superiore (radice del progetto, es. `figure/` con l'exe in una sottocartella) ⇒ immagine mostrata | R | `Immagine_percorso_relativo_alla_radice` | [x] | | MD-29 | Se il file esiste in più cartelle, vince la più vicina all'exe; se non esiste in nessuna ⇒ testo alternativo | R | `Immagine_cartella_piu_vicina` | [x] | | MD-30 | Struttura tipo wiki (catalogo JSON + cartella immagini + risposta con immagine, didascalia e link `[[fig-NNN]]` sotto): l'immagine compare e la didascalia resta testo; l'app non legge il JSON | R | `Immagine_risposta_stile_wiki` | [x] | | MD-15 | HTML grezzo disabilitato: i tag compaiono come testo, non come elementi | R | `Html_disabilitato` | [ ] | | MD-16 | Cambio di tema (chiaro/scuro): i colori si aggiornano ricostruendo la vista | R | `Cambio_tema` | [ ] | | MD-17 | Markdown malformato o blocchi sconosciuti non fanno crashare | R | `Markdown_malformato` | [ ] | | MD-18 | Testo selezionabile | R | `Testo_selezionabile` | [ ] | ### Finestra e interazione — `Gui/MainWindow*`, `Gui/App.axaml`, `Gui/ConfirmDialog.cs` | ID | Requisito | Liv. | Test | | |---|---|---|---|---| | UI-01 | La finestra si apre e mostra la schermata iniziale (logo, titolo, sottotitolo, nessun suggerimento) | G | `Apertura_schermata_iniziale` | [ ] | | UI-02 | Proporzioni 16:10, 75% dell'area utile (max 1440), minimo 900x600; senza schermo primario 1280x800 | G | `Dimensioni_finestra` | [ ] | | UI-03 | `Invio` invia, `Maiusc+Invio` va a capo, nessun invio a campo vuoto | G | `Tasti_invio` | [ ] | | UI-04 | Il pulsante Invia è disabilitato con campo vuoto o in attesa | G | `Pulsante_invia` | [ ] | | UI-05 | I messaggi hanno aspetto per ruolo: utente, Claude (con Copia), errore | G | `Template_per_ruolo` | [ ] | | UI-06 | La conversazione scorre in fondo a ogni nuovo messaggio | G | `Scorrimento_in_fondo` | [ ] | | UI-07 | Indicatore di attesa visibile solo durante l'elaborazione | G | `Indicatore_attesa` | [ ] | | UI-08 | Elenco chat: voce attiva evidenziata, clic apre, ✕ elimina; "Nessuna chat salvata" se vuoto | G | `Elenco_chat` | [ ] | | UI-09 | Nel riquadro Impostazioni il campo "Altro ID…" compare solo con quella voce del modello | G | `Campo_modello_personalizzato` | [ ] | | UI-10 | Il pulsante a tre linee mostra/nasconde la barra laterale | G | `Mostra_nascondi_barra` | [ ] | | UI-11 | Dialogo di conferma: restituisce vero con Elimina, falso con Annulla o chiusura | G | `Dialogo_conferma` | [ ] | | UI-12 | Accessibilità: ogni pulsante a sola icona e il campo messaggio hanno `AutomationProperties.Name` | G | `Nomi_accessibili` | [ ] | | UI-13 | Tema: sia il chiaro sia lo scuro risolvono tutti i token usati (nessuna risorsa mancante) | G | `Token_tema_completi` | [ ] | | UI-14 | Focus sul campo messaggio all'apertura, dopo la risposta e dopo "Nuova chat" | G | `Focus_campo` | [ ] | | UI-16 | Il pulsante Impostazioni (in alto a destra) apre il riquadro con Modello ed Effort; `Esc` lo chiude | G | `Riquadro_impostazioni` | [ ] | | UI-17 | Il composer non contiene selettori di modello ed effort | G | `Composer_senza_selettori` | [ ] | | UI-18 | Le scelte fatte nel riquadro arrivano ai flag `--model`/`--effort` del messaggio successivo | G/U | `Impostazioni_applicate_all_invio` | [ ] | | UI-15 | Il nome dell'app compare nel titolo della finestra e nella barra laterale | G | `Nome_app_visibile` | [ ] | ### Eseguibile pubblicato Righe WIN: console su Windows (ADR-0005). | ID | Requisito | Liv. | Test | | |---|---|---|---|---| | WIN-01 | L'eseguibile `win-x64` pubblicato ha sottosistema "Windows GUI" (intestazione PE); quello `linux-x64` non è interessato | E | `Sottosistema_gui` | [ ] | | WIN-02 | CLI con output reindirizzato su file o pipe: l'output arriva, handle ereditati non toccati | E | `Cli_con_output_reindirizzato` | [ ] | | WIN-03 | CLI da un terminale: output visibile (`AttachConsole` + `CONOUT$`); senza terminale padre l'attacco fallisce in silenzio e l'app prosegue | E (manuale su Windows nativo) | `Cli_con_terminale_padre` | [ ] | | WIN-04 | Exit code corretto quando il chiamante attende il processo (`start /wait`, `Start-Process -Wait`) | E (manuale su Windows nativo) | `ExitCode_con_attesa` | [ ] | | WIN-05 | Il codice di `ConsoleHost` non fa nulla su Linux/macOS | U | `ConsoleHost_non_windows` | [ ] | | ID | Requisito | Liv. | Test | | |---|---|---|---|---| | PUB-01 | Il publish single-file `linux-x64` si compila e la CLI risponde con `FakeClaude` | E | `Publish_linux_smoke` | [ ] | | PUB-02 | Il publish single-file `win-x64` si compila e la CLI risponde con `FakeClaude` | E | `Publish_windows_smoke` | [ ] | | PUB-03 | Le librerie native (Skia/HarfBuzz) vengono estratte e la GUI si avvia (provata con timeout) | E | `Gui_si_avvia` | [ ] | ### Più motori (ADR-0002) — da compilare quando si implementa la Fase 4 Per **ogni** motore, la stessa batteria di contratto: | ID | Requisito | Liv. | | |---|---|---|---| | EN-01 | Argomenti del primo messaggio e della ripresa (Claude: CL-02; Codex: CX-01, CX-02) | U | [ ] | | EN-02 | Rilevamento nel PATH; assente ⇒ disabilitato e messaggio chiaro | U/P | [ ] | | EN-03 | Lettura dell'id di sessione dall'output (Codex: CX-05) | U | [ ] | | EN-04 | Parser dell'output → markdown, con output di esempio reali salvati nei test | U | [ ] | | EN-05 | Modelli ed effort mostrati secondo il motore | U/G | [ ] | | EN-06 | Il JSON della chat memorizza il motore; cambiare motore apre una nuova chat | U | [ ] | | EN-07 | Politica dei permessi: nessun flag che salta i controlli senza scelta esplicita | U | [ ] | | EN-08 | Impostazioni persistite e rilette | U | [ ] | | EN-09 | Gestione del blocco di `agy -p` con output rediretto (timeout, errore chiaro) | P | [ ] | | EN-10 | `Engines.Find` per id (`claude`, `codex`, sconosciuto ⇒ nullo); l'id salvato nei JSON resta stabile | U | [ ] | | EN-11 | Cambiare CLI nelle Impostazioni apre una nuova chat con quel motore; modello ed effort tornano ai valori iniziali del nuovo motore (modello più piccolo, `low`); liste secondo il motore | U | [ ] | | EN-12 | Motore non installato: etichetta "non installato", non selezionabile; il VM rifiuta la selezione | U/G | [ ] | | EN-13 | L'interruttore CLI è disabilitato durante l'attesa | U/G | [ ] | | EN-14 | Aprendo una chat salvata il selettore passa al motore della chat; sessione e `started` coerenti | U | [ ] | | EN-15 | Etichetta autore, segnaposto, messaggio d'attesa ed elenco chat mostrano il nome del motore | U/G | [ ] | | EN-16 | Dopo il primo invio con Codex il `SessionId` è quello letto dall'output e il secondo invio usa la ripresa; con errore al primo invio resta senza sessione | U | [ ] | | EN-17 | CLI `--engine "prompt"`; id sconosciuto o prompt mancante ⇒ uso su stderr ed exit 2; senza `--engine` ⇒ Claude Code | E | [ ] | | EN-18 | Rilevamento nel PATH: nome esatto su Linux, `.exe` su Windows; cartella inesistente nel PATH ignorata | U | [ ] | ### FakeClaude — `tests/FakeClaude` | ID | Requisito | Liv. | Test | | |---|---|---|---|---| | FK-01 | Registra argomenti (anche con virgolette e spazi, uno per elemento), cartella di lavoro e stdin | P | `Registra_argomenti_cwd_e_stdin` | [x] | | FK-02 | Scrive su stdout e stderr il testo configurato | P | `Output_e_stderr_configurati` | [x] | | FK-03 | L'exit code configurato è quello del processo | P | `ExitCode_configurato` | [x] | | FK-04 | Il ritardo configurato è rispettato | P | `Ritardo_configurato` | [x] | | FK-05 | Termina solo dopo la chiusura di stdin | P | `Attende_la_chiusura_di_stdin` | [x] | ## Come si eseguono (a implementazione avvenuta) Comandi previsti, da confermare con ADR-0003 → "Da verificare": - Tutti i test: `dotnet test` - Con copertura e soglie: `dotnet test --collect:"XPlat Code Coverage"` e controllo delle soglie della tabella sopra - Solo una categoria: `dotnet test --filter "Category=Unit"` (o `Gui`, `Process`, `E2E`) Su Linux i test GUI girano senza display (Headless). ## Definition of Done di una modifica - [ ] Doc aggiornati (MVP, ROADMAP, ADR se serve) - [ ] Righe nuove o modificate nella matrice, con ID - [ ] Test scritti e verdi su Windows e Linux - [ ] Soglie di copertura rispettate - [ ] Nessun requisito della modifica resta con la casella `[ ]` senza motivo scritto