GTK4/VTE terminal workspace with MCP control plane and UI screenshots
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

14 KiB

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: truePASS

  • S3: read_tab(tab_id) sulla stessa tab Verifica: output VTE grezzo contiene test_s2PASS

  • 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_promptPASS

  • 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: falsePASS 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.