3 changed files with 659 additions and 35 deletions
@ -0,0 +1,383 @@
|
||||
# mcp-hal9002 Test Report |
||||
|
||||
Data: 15 marzo 2026 — aggiornato al 16 marzo 2026 |
||||
|
||||
## Obiettivo |
||||
|
||||
Tracciare gli scenari di test eseguiti sui tool MCP di `mcp-hal9002`, con esito, note operative e anomalie osservate. Questo documento viene aggiornato a ogni campagna di test significativa. |
||||
|
||||
## Ambito Coperto |
||||
|
||||
Tool coperti durante le sessioni: |
||||
|
||||
- `gui_status()` |
||||
- `open_gui()` implicito tramite `open_tab(...)` |
||||
- `close_gui()` |
||||
- `open_tab(...)` |
||||
- `list_tabs()` |
||||
- `focus_tab(...)` |
||||
- `exec_command(...)` |
||||
- `read_tab(...)` |
||||
- `read_last_command_result(...)` |
||||
- `wait_for_command(...)` |
||||
- `wait_for_running_command(...)` |
||||
- `wait_for_command_result(...)` |
||||
- `wait_for_prompt(...)` |
||||
- `capture_screenshot(...)` |
||||
- `close_tab(...)` |
||||
|
||||
## Matrice Scenari |
||||
|
||||
### 1. Lifecycle GUI (T1) |
||||
|
||||
Stato iniziale verificato con GUI già in esecuzione e tab residue da test precedenti. |
||||
|
||||
Scenari eseguiti: |
||||
|
||||
- chiusura completa della GUI condivisa con `close_gui()` |
||||
- verifica di `running=false` dopo `close_gui()` |
||||
- riavvio implicito della GUI tramite `open_tab(...)` |
||||
- verifica del riuso dell'istanza condivisa via socket locale |
||||
- `gui_status()` con GUI spenta → `running=false` |
||||
- `gui_status()` con GUI attiva → campi geometria e `tab_count` presenti |
||||
|
||||
Esito: `PASS` |
||||
|
||||
Note: |
||||
|
||||
- la GUI si spegne correttamente e il socket viene rimosso |
||||
- una nuova `open_tab(...)` rilancia la GUI come previsto |
||||
- `gui_status()` non lancia la GUI — comportamento corretto |
||||
|
||||
### 2. Lifecycle Tab (T2) |
||||
|
||||
Scenari eseguiti: |
||||
|
||||
- apertura di più tab con `title` differenti |
||||
- apertura di tab con `cwd` esplicito |
||||
- verifica elenco tab con `list_tabs()` |
||||
- cambio tab attiva con `focus_tab(...)` |
||||
- chiusura di una tab intermedia con `close_tab(...)` |
||||
- verifica consistenza degli ID residui dopo la chiusura |
||||
- `focus_tab(...)` su `tab_id` inesistente → RuntimeError atteso |
||||
|
||||
Esito: `PASS` |
||||
|
||||
Note: |
||||
|
||||
- gli ID tab restano coerenti dopo chiusure intermedie |
||||
- lo stato `active` segue correttamente la tab focalizzata |
||||
- gli errori su `tab_id` inesistente sono uniformi tra tutti i tool tab-scoped |
||||
|
||||
### 3. Esecuzione Comandi con Auto Submit (T3) |
||||
|
||||
Scenari eseguiti: |
||||
|
||||
- `exec_command(tab_id, "pwd", auto_submit=True)` su tab in `cwd=/home/enne2` |
||||
- `exec_command(tab_id, "pwd", auto_submit=True)` su tab in `cwd=/home/enne2/dev/mcp-hal9002` |
||||
- `exec_command(tab_id, "whoami", auto_submit=True)` |
||||
- `exec_command(tab_id, "ls /definitely-missing-path", auto_submit=True)` |
||||
- `exec_command(tab_id, "uname -r", auto_submit=True)` |
||||
|
||||
Esito: `PASS` |
||||
|
||||
Note: |
||||
|
||||
- `auto_submit=True` invia il comando e attende solo la sua conclusione |
||||
- il `cwd` osservato nei risultati coincide con la directory della tab |
||||
- gli errori runtime del comando (`exit_code=1`) sono tracciati correttamente |
||||
- `after_sequence` restituito è pronto per passare a `wait_for_command_result` |
||||
|
||||
### 4. Pattern after_sequence (T4) |
||||
|
||||
Scenari eseguiti: |
||||
|
||||
- submit di `ls /tmp` con `auto_submit=True`, salvataggio di `after_sequence` |
||||
- chiamata a `wait_for_command_result(tab_id, after_sequence=...)` con il valore ottenuto |
||||
- submit di un secondo comando e verifica che `after_sequence` diverso isoli risultati separati |
||||
|
||||
Esito: `PASS` |
||||
|
||||
Note: |
||||
|
||||
- `after_sequence` permette di isolare esattamente il comando appena emesso |
||||
- i risultati includono `sequence`, `command`, `cwd`, `cwd_after`, `exit_code`, `duration_seconds`, `text` |
||||
- il campo `text` contiene l'output catturato tra i marker shell, pulito da prompt e echo |
||||
|
||||
### 5. Lettura Output e Risultati (T5) |
||||
|
||||
Scenari eseguiti: |
||||
|
||||
- lettura scrollback grezzo con `read_tab(...)` dopo un `exec_command` |
||||
- lettura ultimo risultato tracciato con `read_last_command_result(...)` |
||||
- attesa del risultato con `wait_for_command_result(...)` |
||||
- confronto metadati tra `read_last_command_result(...)` e `wait_for_command_result(...)` |
||||
- distinzione uso: `read_tab` per output grezzo, `read_last_command_result` per metadati strutturati |
||||
|
||||
Esito: `PASS` |
||||
|
||||
Note: |
||||
|
||||
- `read_tab` restituisce testo grezzo con prompt e echo: utile per debug e sessioni delegate |
||||
- `read_last_command_result` fornisce output pulito tra i marker hook: utile per parsing strutturato |
||||
- i due metodi convergono sugli stessi metadati per lo stesso comando completato |
||||
- `read_tab` è l'unica opzione pratica nelle sessioni delegate dove i command events non vengono emessi |
||||
|
||||
### 6. Attese e Timeout (T6) |
||||
|
||||
Scenari eseguiti: |
||||
|
||||
- pausa esplicita con `wait_for_command(delay_seconds=2.0)` |
||||
- timeout breve su comando lungo con `wait_for_running_command(tab_id, timeout=0.5)` → RuntimeError atteso |
||||
- `wait_for_running_command(tab_id)` su tab idle → RuntimeError "no tracked command is currently running" |
||||
- attesa lunga di completamento sullo stesso comando in esecuzione |
||||
- attesa di risultato con `after_sequence` corretto su `du -ah /usr` |
||||
|
||||
Esito: `PASS` |
||||
|
||||
Note: |
||||
|
||||
- `wait_for_running_command` su tab idle fallisce immediatamente come previsto |
||||
- il timeout breve fallisce correttamente mentre il comando è ancora in corso |
||||
- l'attesa lunga porta al completamento atteso |
||||
- `wait_for_running_command(...)` e `wait_for_command_result(...)` restituiscono metadati coerenti sullo stesso evento finale |
||||
|
||||
### 7. Screenshot — Target e Naming (T7 / T8) |
||||
|
||||
Scenari eseguiti: |
||||
|
||||
- screenshot `target="window"` con overlay diagnostico |
||||
- screenshot `target="tab"` con overlay diagnostico |
||||
- screenshot `target="tab-container"` con overlay diagnostico |
||||
- entrambi `target="tab"` e `target="tab-container"` sullo stesso `tab_id`, stesso secondo → collisione naming confermata |
||||
- stesso scenario con `path` esplicito per ciascuna chiamata → nessuna collisione |
||||
|
||||
Esito: `PASS` con anomalia nota (vedi Anomalia 1) |
||||
|
||||
Note: |
||||
|
||||
- la cattura funziona per tutti i target provati |
||||
- i metadata sidecar JSON risultano popolati correttamente |
||||
- la collisione di naming è riproducibile e confermata |
||||
- il workaround con `path` esplicito risolve completamente la collisione |
||||
- il campo `summary` nel risultato contiene path, dimensioni, tipo widget e renderer |
||||
|
||||
### 8. Errori e Guardrail (T8) |
||||
|
||||
Scenari eseguiti: |
||||
|
||||
- `exec_command(..., auto_submit=True)` con shell syntax bloccata: `ls | head` |
||||
- `exec_command(..., auto_submit=True)` con shell syntax bloccata: `echo hi && echo bye` |
||||
- `exec_command(..., auto_submit=True)` con comando vietato: `python3 -c 'print(1)'` |
||||
- `exec_command(..., auto_submit=True)` con comando non in whitelist: `jq ...` |
||||
- `focus_tab(...)` su `tab_id` inesistente |
||||
- `read_tab(...)` su `tab_id` inesistente |
||||
- `close_tab(...)` su `tab_id` inesistente |
||||
|
||||
Esito: `PASS` |
||||
|
||||
Note: |
||||
|
||||
- i messaggi d'errore sono coerenti con le regole dei guardrail |
||||
- gli errori vengono sollevati prima del submit, non dopo |
||||
- non sono emersi stati corrotti del terminale dopo gli errori lato tool |
||||
|
||||
### 9. Comportamento open_tab(command=...) (T9) |
||||
|
||||
Scenari eseguiti: |
||||
|
||||
- apertura tab con `open_tab(command="uname -r")` (comando terminante) |
||||
- verifica con `read_last_command_result(...)` dopo ~500 ms |
||||
- apertura tab con `open_tab(command="bash --norc")` (sub-shell persistente) |
||||
- tentativo di `read_last_command_result(...)` nella tab con sub-shell |
||||
|
||||
Esito: `PASS` con distinzione critica |
||||
|
||||
Note: |
||||
|
||||
- **comandi terminanti** (`uname -r`, `ls /tmp`, ecc.) iniettati via `command=` **sono tracciati** come |
||||
`sequence=1` con i normali metadati command event |
||||
- **sub-shell persistenti** (`bash`, `ssh`, `python3` REPL) avviate via `command=` **non sono tracciate**: |
||||
la sub-shell non eredita l'hook MCP, quindi `read_last_command_result` solleva RuntimeError |
||||
- questa distinzione era documentata in modo errato nel docstring di `read_last_command_result` — corretta durante il test |
||||
|
||||
### 10. Submit Manuale (T11) |
||||
|
||||
Scenari eseguiti: |
||||
|
||||
- `exec_command(tab_id, "echo 'ciao dal test'", auto_submit=False)` → ritorno bloccante |
||||
- utente preme Invio nella GUI dopo 2–3 secondi |
||||
- verifica che `exec_command` ritorni `submitted_manually=true` |
||||
- verifica campo `newline_ignored` nel risultato |
||||
- chiamata a `wait_for_command_result(tab_id, after_sequence=...)` sul risultato |
||||
|
||||
Esito: `PASS` |
||||
|
||||
Note: |
||||
|
||||
- `exec_command` rimane in attesa bloccante fino a che l'utente non preme Invio nel terminale |
||||
- il campo `submitted_manually=true` identifica correttamente la modalità |
||||
- `after_sequence` è disponibile anche in modalità manuale per filtrare il risultato atteso |
||||
- `newline_ignored=false` in modalità manuale (il newline è parte dell'azione utente) |
||||
|
||||
### 11. Attesa Comando Lungo con wait_for_running_command (T12) |
||||
|
||||
Scenari eseguiti: |
||||
|
||||
- utente digita e invia `sleep 4` nella GUI manualmente |
||||
- chiamata immediata a `wait_for_running_command(tab_id)` dal tool |
||||
- verifica che il tool attenda il completamento del sleep |
||||
- verifica metadati nel risultato: `duration_seconds` ≥ 4 |
||||
|
||||
Esito: `PASS` |
||||
|
||||
Note: |
||||
|
||||
- `wait_for_running_command` rileva correttamente lo stato `running` e aspetta il completamento |
||||
- il campo `duration_seconds` riflette il tempo effettivo di esecuzione |
||||
- questo scenario non ha `after_sequence`, dimostrando il caso d'uso corretto di `wait_for_running_command` |
||||
|
||||
### 12. Sessioni Delegate — in_delegated_session (T13 + S1–S7) |
||||
|
||||
Questa sezione copre la sub-shell avviata manualmente e il flag `in_delegated_session`. |
||||
|
||||
#### T13 — Primo scenario sub-shell manuale |
||||
|
||||
Scenari eseguiti: |
||||
|
||||
- `exec_command(tab_id, "bash --norc", auto_submit=False)` con utente che preme Invio → sub-shell attiva |
||||
- verifica che lo stato del tab diventi `interactive-session` |
||||
- iniezione di `echo test` via `exec_command(tab_id, "echo test", auto_submit=False)` nella sub-shell |
||||
- verifica che la risposta contenga `in_delegated_session: true` |
||||
|
||||
Esito: `PASS` |
||||
|
||||
Note: |
||||
|
||||
- il flag `in_delegated_session` è presente nella risposta sia per `auto_submit=True` che `False` |
||||
- l'iniezione nella sub-shell funziona a livello VTE ma non produce command events tracciati |
||||
|
||||
#### Campagna sub-shell S1–S7 |
||||
|
||||
Scenari eseguiti: |
||||
|
||||
- **S1**: apertura tab fresca, submit manuale di `bash --norc` via GUI |
||||
Verifica: `running_command_status(tab_id)` riporta `state="interactive-session"` → `PASS` |
||||
|
||||
- **S2**: `exec_command(tab_id, "echo test_s2", auto_submit=False)` nella sub-shell attiva, |
||||
utente preme Invio |
||||
Verifica: risposta contiene `in_delegated_session: true` → `PASS` |
||||
|
||||
- **S3**: `read_tab(tab_id)` sulla stessa tab |
||||
Verifica: output VTE grezzo contiene `test_s2` → `PASS` |
||||
|
||||
- **S4**: `wait_for_running_command(tab_id)` → RuntimeError immediato per stato `interactive-session` |
||||
`wait_for_command_result(tab_id, after_sequence=..., timeout=3)` → RuntimeError per timeout |
||||
Verifica: entrambi falliscono come previsto; il percorso corretto è `read_tab` + `wait_for_prompt` → `PASS` |
||||
|
||||
- **S5**: utente digita `exit` nella sub-shell → ritorno alla shell parent MCP |
||||
`wait_for_prompt(tab_id, timeout=10)` → rilevamento del prompt parent |
||||
Verifica: `state="prompt"`, `last_line` contiene il prompt della shell MCP → `PASS` |
||||
|
||||
- **S6**: `exec_command(tab_id, "whoami", auto_submit=True)` dopo exit dalla sub-shell |
||||
Verifica: funziona normalmente, `in_delegated_session: false` → `PASS` |
||||
Nota: intervento manuale accidentale nella GUI ha aggiunto testo spurio, rimosso con Ctrl+C |
||||
|
||||
- **S7**: pulizia — `close_tab(tab_id)` |
||||
Verifica: tab chiusa correttamente → `PASS` |
||||
|
||||
Esito complessivo: `PASS` |
||||
|
||||
Note tecniche: |
||||
|
||||
- la GUI traccia le sub-shell in `TabState` con campi `delegated_session_*` |
||||
- `rpc_exec` in `gui.py` rileva `state="interactive-session"` e imposta `pending_submit_in_delegated_session=True` |
||||
- il flag `in_delegated_session` viene propagato in entrambi i path di `exec_command` in `server.py` |
||||
- `wait_for_running_command` solleva immediatamente per `interactive-session` (comportamento corretto) |
||||
- `wait_for_prompt` è il tool di osservazione raccomandato nelle sessioni delegate |
||||
|
||||
## Anomalie Osservate |
||||
|
||||
### 1. Collisione Naming Screenshot |
||||
|
||||
Stato: **APERTA** (workaround disponibile) |
||||
|
||||
Sintomo: |
||||
|
||||
- catture con `target="tab"` e `target="tab-container"` sullo stesso `tab_id` entro lo stesso secondo |
||||
producono lo stesso path di default; la seconda sovrascrive la prima silenziosamente |
||||
|
||||
Impatto: |
||||
|
||||
- un file PNG o JSON può sovrascrivere l'altro |
||||
|
||||
Workaround confermato: |
||||
|
||||
- passare `path` esplicito a entrambe le chiamate risolve completamente il problema — verificato in T7/T8 |
||||
|
||||
Area coinvolta: |
||||
|
||||
- generazione path di default screenshot in `gui.py` |
||||
|
||||
### 2. Tracciamento Startup Command Interattivi |
||||
|
||||
Stato: **PARZIALMENTE RISOLTA** |
||||
|
||||
Sintomo originale: |
||||
|
||||
- `open_tab(command="bash")` produceva output ma non rendeva osservabile il prompt né i command events |
||||
|
||||
Aggiornamento post-T9: |
||||
|
||||
- **comandi terminanti** (`uname -r`, `ls`, ecc.) iniettati via `open_tab(command=...)` **sono confermati come tracciati** |
||||
correttamente (`sequence=1`, `exit_code=0`) |
||||
- **sub-shell persistenti** (`bash`, `python3 REPL`, `ssh`) avviate via `open_tab(command=...)` rimangono non tracciate |
||||
perché la sub-shell non eredita l'hook MCP |
||||
|
||||
Impatto residuo: |
||||
|
||||
- solo il caso sub-shell persistente via `open_tab(command=...)` è ancora non tracciato |
||||
- il caso più utile (sub-shell avviata via `exec_command` manuale) è completamente supportato |
||||
tramite il flag `in_delegated_session` + `read_tab` + `wait_for_prompt` |
||||
|
||||
### 3. Timestamps di Esecuzione da Ricontrollare |
||||
|
||||
Stato: **APERTA** (impatto basso in condizioni normali) |
||||
|
||||
Sintomo: |
||||
|
||||
- alcuni `started_at` e `duration_seconds` osservati sembrano partire prima del submit effettivo percepito |
||||
|
||||
Impatto: |
||||
|
||||
- i metadati temporali potrebbero risultare meno affidabili del previsto nei report di comando |
||||
|
||||
Area coinvolta: |
||||
|
||||
- hook shell che scrive transcript ed eventi |
||||
|
||||
## Casi Ancora da Testare Manualmente |
||||
|
||||
Tutti i casi elencati nella versione precedente di questo documento sono stati coperti: |
||||
|
||||
- `exec_command(..., auto_submit=False)` con modifica del comando → **coperto in T11** |
||||
- `newline=True` ignorato in modalità manuale → **confermato in T11** (`newline_ignored` nel risultato) |
||||
- sessioni delegate (sub-shell, SSH) avviate con submit manuale → **coperto in T13 + S1–S7** |
||||
- uso combinato di `wait_for_prompt(...)` e `wait_for_running_command(...)` in sessioni interattive → **coperto in S4+S5** |
||||
|
||||
Nessun caso rilevante rimane da testare nelle condizioni attuali. |
||||
|
||||
## Stato Finale Ambiente di Test |
||||
|
||||
Pulizia eseguita dopo ogni campagna: |
||||
|
||||
- chiusura delle tab create per i test |
||||
- verifica finale con sola tab Home presente o GUI spenta |
||||
|
||||
Esito cleanup: `PASS` per tutte le campagne |
||||
|
||||
## Conclusione |
||||
|
||||
La maggior parte dei tool MCP testati risulta funzionante nei casi non interattivi e nei flussi standard di osservazione del terminale. Le sessioni delegate (sub-shell, SSH) sono ora supportate tramite il flag `in_delegated_session` nell'output di `exec_command` e l'uso combinato di `read_tab` + `wait_for_prompt`. |
||||
|
||||
I problemi residui aperti sono due: la collisione di naming screenshot (workaround disponibile con `path` esplicito) e la piccola incertezza sui timestamp di esecuzione. |
||||
Loading…
Reference in new issue