From 00a32220d1401a64fda89f986c8bfb39a6611fd4 Mon Sep 17 00:00:00 2001 From: Blomios Date: Wed, 9 Sep 2026 14:30:39 +0200 Subject: [PATCH 1/2] =?UTF-8?q?fix(terminals):=20le=20reattach=20xterm=20n?= =?UTF-8?q?e=20r=C3=A9injecte=20plus=20les=20r=C3=A9ponses=20synth=C3=A9ti?= =?UTF-8?q?ques=20du=20replay=20scrollback=20=E2=80=94=20#292=20(term.onDa?= =?UTF-8?q?ta=20n'est=20actif=20qu'apr=C3=A8s=20le=20callback=20final=20du?= =?UTF-8?q?=20term.write(scrollback):=20les=20r=C3=A9ponses=20aux=20requ?= =?UTF-8?q?=C3=AAtes=20historiques=20ESC[1;1R/modes=20ne=20peuplent=20plus?= =?UTF-8?q?=20pending=20ni=20ne=20sont=20r=C3=A9=C3=A9crites=20dans=20le?= =?UTF-8?q?=20PTY=20par=20adopt,=20ce=20qui=20bouclait=20CPU=20100%=20sur?= =?UTF-8?q?=20les=20agents=20Git=20TUI=20Native=20d'UnitySoundTool/UnityPl?= =?UTF-8?q?ugin;=20activation=20de=20l'input=20aussi=20sur=20les=20chemins?= =?UTF-8?q?=20fallback=20reattach-=C3=A9chou=C3=A9=20et=20terminal=20neuf;?= =?UTF-8?q?=20test=20de=20r=C3=A9gression:=20z=C3=A9ro=20write=20PTY=20pen?= =?UTF-8?q?dant=20le=20replay=20synth=C3=A9tique=20puis=20exactement=20un?= =?UTF-8?q?=20write=20pour=20Hello\r;=20QA=20verte:=203=20fichiers/27=20te?= =?UTF-8?q?sts=20TerminalView,=20npm=20run=20build=20frontend,=20AppImage?= =?UTF-8?q?=200.3.0=20rebuild=20sha256=204165521e...;=20r=C3=A9serve:=20re?= =?UTF-8?q?pro=20live=20UI=20trois=20projets=20non=20valid=C3=A9e=20depuis?= =?UTF-8?q?=20sandbox)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TerminalView.scrollback.test.tsx | 80 +++++++++++++++++-- .../src/features/terminals/TerminalView.tsx | 73 +++++++++++------ 2 files changed, 121 insertions(+), 32 deletions(-) diff --git a/frontend/src/features/terminals/TerminalView.scrollback.test.tsx b/frontend/src/features/terminals/TerminalView.scrollback.test.tsx index 2341f90e..bdb0ad3d 100644 --- a/frontend/src/features/terminals/TerminalView.scrollback.test.tsx +++ b/frontend/src/features/terminals/TerminalView.scrollback.test.tsx @@ -1,11 +1,18 @@ -import { describe, it, expect, vi } from "vitest"; -import { render } from "@testing-library/react"; +import { beforeEach, describe, it, expect, vi } from "vitest"; +import { render, waitFor } from "@testing-library/react"; -import type { Gateways } from "@/ports"; +import type { Gateways, ReattachResult, TerminalHandle } from "@/ports"; import { MockTerminalGateway } from "@/adapters/mock"; import { DIProvider } from "@/app/di"; const terminalOptions: unknown[] = []; +const xtermState: { + keyHandler: ((data: string) => void) | null; + answerOnWrite: string | null; +} = { + keyHandler: null, + answerOnWrite: null, +}; vi.mock("@xterm/xterm", () => ({ Terminal: class { @@ -18,11 +25,19 @@ vi.mock("@xterm/xterm", () => ({ loadAddon() {} open() {} - onData() { + onData(callback: (data: string) => void) { + xtermState.keyHandler = callback; return { dispose() {} }; } - write() {} - input() {} + write(_data: string | Uint8Array, callback?: () => void) { + if (xtermState.answerOnWrite) { + xtermState.keyHandler?.(xtermState.answerOnWrite); + } + callback?.(); + } + input(data: string) { + xtermState.keyHandler?.(data); + } focus() {} dispose() {} }, @@ -49,6 +64,12 @@ import { TERMINAL_SCROLLBACK_LINES, } from "./TerminalView"; +beforeEach(() => { + terminalOptions.length = 0; + xtermState.keyHandler = null; + xtermState.answerOnWrite = null; +}); + describe("TerminalView scrollback", () => { it("configures xterm with a deep scrollback for chatty OpenCode agents", async () => { const gateways = { terminal: new MockTerminalGateway() } as unknown as Gateways; @@ -64,4 +85,51 @@ describe("TerminalView scrollback", () => { }); expect(TERMINAL_SCROLLBACK_LINES).toBeGreaterThan(1_000); }); + + it("does not relay synthetic xterm answers from reattach replay, then writes human input once", async () => { + const write = vi.fn().mockResolvedValue(undefined); + const handle: TerminalHandle = { + sessionId: "live-loop-regression", + write, + resize: vi.fn().mockResolvedValue(undefined), + detach: vi.fn(), + close: vi.fn().mockResolvedValue(undefined), + }; + const reattach = vi.fn(async () => { + const result: ReattachResult = { + handle, + // Represents retained TUI output containing a terminal query. The xterm + // stub answers from write(), exactly where the real ESC[1;1R burst was + // produced on the affected Git agents. + scrollback: new TextEncoder().encode("\x1b[6n"), + }; + return result; + }); + const gateways = { + terminal: { + openTerminal: vi.fn(), + reattach, + closeTerminal: vi.fn(), + }, + } as unknown as Gateways; + xtermState.answerOnWrite = "\x1b[1;1R"; + + render( + + + , + ); + + await waitFor(() => expect(reattach).toHaveBeenCalledTimes(1)); + await waitFor(() => expect(xtermState.keyHandler).not.toBeNull()); + + // The response synthesized while replaying retained output never reaches + // pending/adopt and therefore never loops back into the PTY. + expect(write).not.toHaveBeenCalled(); + + xtermState.keyHandler!("Hello\r"); + + await waitFor(() => expect(write).toHaveBeenCalledTimes(1)); + expect(new TextDecoder().decode(write.mock.calls[0][0])).toBe("Hello\r"); + }); }); diff --git a/frontend/src/features/terminals/TerminalView.tsx b/frontend/src/features/terminals/TerminalView.tsx index d373cfd0..a24b35fc 100644 --- a/frontend/src/features/terminals/TerminalView.tsx +++ b/frontend/src/features/terminals/TerminalView.tsx @@ -238,30 +238,34 @@ export function TerminalView({ // not race the injected text. Keystrokes arriving before the PTY opened are // buffered. The output path below is unchanged in both modes. let pending = ""; - const onKey = term.onData((data) => { - const portal = portalRef.current; - if (agentModeRef.current && portal) { - // Always report for counting (even while suspended — the portal decides - // what counts), then drop the relay while the portal is injecting. - portal.onHumanData(data); - if (portal.isSuspended()) return; - } - if (handle) void handle.write(encoder.encode(data)); - else pending += data; - }); + let onKey: { dispose: () => void } | null = null; + const activateInput = () => { + if (disposed || onKey) return; + onKey = term.onData((data) => { + const portal = portalRef.current; + if (agentModeRef.current && portal) { + // Always report for counting (even while suspended — the portal decides + // what counts), then drop the relay while the portal is injecting. + portal.onHumanData(data); + if (portal.isSuspended()) return; + } + if (handle) void handle.write(encoder.encode(data)); + else pending += data; + }); - // Publish the input API now that the keystroke relay above is live, so an - // injected key is handled exactly like a typed one. Guarded on `disposed`: - // the caller may still hold this object after the cell unmounts, and driving - // a disposed xterm throws. - onReadyRef.current?.({ - send: (data) => { - if (!disposed) term.input(data); - }, - focus: () => { - if (!disposed) term.focus(); - }, - }); + // Publish the input API only once the keystroke relay is live, so an + // injected key is handled exactly like a typed one. Guarded on `disposed`: + // the caller may still hold this object after the cell unmounts, and driving + // a disposed xterm throws. + onReadyRef.current?.({ + send: (data) => { + if (!disposed) term.input(data); + }, + focus: () => { + if (!disposed) term.focus(); + }, + }); + }; const onData = (bytes: Uint8Array) => { if (!disposed) term.write(bytes); @@ -321,13 +325,27 @@ export function TerminalView({ h.detach(); return; } - if (scrollback.length > 0) term.write(scrollback); - adopt(h); + const finishReattach = () => { + if (disposed) { + h.detach(); + return; + } + // A scrollback replay can make xterm answer historical terminal + // queries (cursor position, device attributes, modes…). Keep the + // input relay absent until xterm confirms the replay is complete, + // otherwise those synthetic answers enter `pending` and are sent + // back into the live PTY by `adopt`, creating a feedback loop. + activateInput(); + adopt(h); + }; + if (scrollback.length > 0) term.write(scrollback, finishReattach); + else finishReattach(); }) .catch(() => { // The session is gone (explicitly closed / exited): fall back to a // fresh terminal so the cell still works. if (disposed) return; + activateInput(); opener({ cwd, rows: term.rows, cols: term.cols }, onData) .then((h) => { onSessionIdRef.current?.(h.sessionId); @@ -336,6 +354,9 @@ export function TerminalView({ .catch(onOpenError); }); } else { + // A fresh terminal has no historical output to replay. Enable input now + // so genuine keystrokes typed while the opener resolves remain buffered. + activateInput(); opener({ cwd, rows: term.rows, cols: term.cols }, onData) .then((h) => { onSessionIdRef.current?.(h.sessionId); @@ -468,7 +489,7 @@ export function TerminalView({ window.removeEventListener("pageshow", scheduleSettledRefit); document.removeEventListener("visibilitychange", scheduleVisibleRefit); window.visualViewport?.removeEventListener("resize", scheduleSettledRefit); - onKey.dispose(); + onKey?.dispose(); portalRef.current?.unbindHandle(); // DETACH, never close: tearing the view down (navigation / layout change) // must leave the backend PTY running so the AI isn't cut off. Killing the From 9fb87a6b746c704359dedf1e757f32632931b49a Mon Sep 17 00:00:00 2001 From: Blomios Date: Wed, 9 Sep 2026 14:30:45 +0200 Subject: [PATCH 2/2] =?UTF-8?q?chore(tickets):=20sync=20store=20local=20?= =?UTF-8?q?=E2=80=94=20#292=20passage=20qa=20apr=C3=A8s=20QA=20verte=20(ca?= =?UTF-8?q?rnet=20complet=20cause/fix/validations,=20index/counter;=20scri?= =?UTF-8?q?pts/=20hors=20p=C3=A9rim=C3=A8tre;=20aucun=20push)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .ideai/tickets/292/carnet.md | 336 +++++++++++++++++++++++++++++++++++ .ideai/tickets/292/issue.md | 17 ++ .ideai/tickets/counter.json | 2 +- .ideai/tickets/index.json | 16 ++ 4 files changed, 370 insertions(+), 1 deletion(-) create mode 100644 .ideai/tickets/292/carnet.md create mode 100644 .ideai/tickets/292/issue.md diff --git a/.ideai/tickets/292/carnet.md b/.ideai/tickets/292/carnet.md new file mode 100644 index 00000000..7c2c59eb --- /dev/null +++ b/.ideai/tickets/292/carnet.md @@ -0,0 +1,336 @@ +--- +issueRef: "#292" +version: 5 +updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} +updatedAt: 1788956518862 +--- +# Carnet #292 — Agents Git TUI Native UnitySoundTool/UnityPlugin + +## Symptôme utilisateur +Le 2026-09-09, l'utilisateur signale que les agents `Git` des projets `UnitySoundTool` (`/home/anthony/Documents/Projects/SoundTool`) et `UnityPlugin` (`/home/anthony/Documents/Projects/UnityPlugin`) bouclent sans répondre avec une entrée simple type `Hello` en TUI Native, et le CPU monte à 100%. L'agent `Git` du projet IdeA courant marche avec une configuration apparemment identique. + +## Cadrage Architect +Architect confirme que les trois agents `Git` utilisent le même profil `5ef495f8-4e6c-58e5-b3c9-f32ec8ffac55` (`GLM5.2`), `command: opencode`, `structuredAdapter: openCode`, config MCP via `opencode.json`. Le libellé `TUI Native` décrit la surface visible, pas un profil Codex brut. + +Contrat attendu si fix runtime: une entrée humaine dans une TUI Native/OpenCode ne peut produire qu'un seul write utilisateur, un seul tour actif par agent, et aucune relance automatique sans action explicite ou erreur terminale exposée. + +## Données collectées +Agents Git: +- IdeA: `1ff94b51-3e17-4a39-8543-7715d1ff0f80`, template `07670754-878b-4a37-8e18-5b061c51cd9b`, syncedTemplateVersion `14`. +- UnityPlugin: `15e9bbaf-8212-4c6e-a592-9d4f7fb44bf1`, même profile/template, syncedTemplateVersion `13`. +- UnitySoundTool: `af772fd8-306e-4ea0-aa20-8f6f537c3377`, même profile/template, syncedTemplateVersion `14`. + +`opencode.json` des trois run dirs est identique hors `--endpoint`, `--project`, `--requester` et `cwd`. Tous pointent vers `/home/anthony/Documents/Projects/IdeA/.appimage-build/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage mcp-server` et le modèle `zai-coding-plan/glm-5.3`. + +Run dirs Git: +- IdeA: `.ideai/run/1ff94b51-3e17-4a39-8543-7715d1ff0f80/{AGENTS.md,opencode.json,.mcp.json}`. +- UnityPlugin: `.ideai/run/15e9bbaf-8212-4c6e-a592-9d4f7fb44bf1/{AGENTS.md,opencode.json}`. +- UnitySoundTool: `.ideai/run/af772fd8-306e-4ea0-aa20-8f6f537c3377/{AGENTS.md,opencode.json}`. + +`AGENTS.md` diff: les projets Unity ont naturellement un contexte projet différent; SoundTool a plusieurs skills Unity dans le contexte. Ce n'est pas censé expliquer une boucle CPU au simple `Hello`. + +États persistés: +- IdeA `.ideai/live-state.json`: Git `done`. +- SoundTool `.ideai/live-state.json`: Git `done`, ticket `c5801e75-14f7-4564-9702-c9667174457e`. +- UnityPlugin `.ideai/live-state.json`: pas d'entrée Git. +- `chat-drafts.json`: drafts vides pour UnityPlugin et SoundTool. +- Les commandes `jq '.tabs, .nodes // empty' .ideai/layouts.json` ont retourné `null` pour UnityPlugin et SoundTool; format à inspecter plus directement si nécessaire. + +Limite de diagnostic: depuis le sandbox Main, `ps` ne voit que les processus du sandbox, donc l'identification du process à 100% (`IdeA`, `opencode`, `node`, `mcp-server`) doit être faite par QA hors sandbox ou via logs applicatifs. + +## Hypothèses actuelles +1. Boucle runtime OpenCode/MCP ou launch/reattach propre aux projets externes: les sockets/cwd changent mais le profil est identique. +2. Boucle frontend TUI Native: double submit, reattach/render loop ou écriture répétée de l'entrée humaine. +3. État projet/layout/session stale non capturé par `live-state.json` simple. +4. UnityPlugin a un template Git v13 au lieu de v14, mais SoundTool échoue aussi en v14, donc ce n'est pas la cause unique. + +## Suite attendue +Demander à Git la branche locale, puis DevBackend pour investigation runtime/PTY/OpenCode/MCP/reconciliation. DevFrontend seulement si DevBackend trouve un double-submit ou une boucle de reattach côté surface. QA devra reproduire sur les trois projets avec le vrai binaire AppImage. + +## Diagnostic DevBackend — 2026-09-09 + +Cause localisée dans la réattache frontend de la TUI native, pas dans le runtime +OpenCode structuré, le lancement PTY ni la réconciliation backend. + +Preuves : + +- `launch_agent` fixe `allow_structured_alongside_pty=false` et ne demande le chemin + structuré que pour une cellule `Chat`. La surface courante ignore ce `cellKind` et + rend toute cellule agent via `TerminalView` : le profil OpenCode est donc lancé une + fois comme TUI native dans un PTY. `OpenCodeSession::send_inner` n'est pas sur ce + chemin utilisateur. +- `LaunchAgent::execute` consulte les registres avant tout I/O et rend la session + existante sur réattache/idempotence. Les tests ciblés de non-respawn sont verts. +- `PortablePtyAdapter::Broadcast::subscribe` remplace le subscriber précédent : il + n'existe qu'un seul pump de sortie actif par PTY après réattache. Le test ciblé est + vert. +- Le log hôte `/home/anthony/.local/share/app.idea.ide/logs/idea.log` corrèle + directement SoundTool Git `af772fd8-306e-4ea0-aa20-8f6f537c3377` : + `front_attached=true` à `1788954519395`, puis 119 appels `write_terminal` sur la + session `557f7aff-2ee5-47f3-9e5b-4d268e255eab` avant le détachement à + `1788954524075`, sans séquence de saisie `Hello`/Entrée dans cette fenêtre. + Le burst commence par des réponses terminal synthétiques xterm : `ESC[1;1R`, + `ESC[?1016;2$y`, `ESC[?2027;0$y`, `ESC[?2031;0$y`, `ESC[?1004;2$y`, + `ESC[?2004;2$y`, `ESC[?2026;2$y`, device attributes et réponses focus. +- Dans `frontend/src/features/terminals/TerminalView.tsx`, `term.onData` est installé + avant la réattache. Le callback met toute donnée dans `pending` tant que le handle + est absent. Après `reattacher(...)`, le code fait `term.write(scrollback)` puis + `adopt(h)` ; le replay des anciennes requêtes terminal provoque donc de nouvelles + réponses xterm, les accumule dans `pending`, puis `adopt` les réinjecte physiquement + dans le PTY. C'est une boucle de replay/réponse propre aux sessions TUI riches en + séquences de contrôle, ce qui explique l'écart entre projets malgré le même profil. + +Le correctif appartient à DevFrontend : pendant le replay du scrollback de réattache, +les données synthétiques produites par xterm ne doivent pas être relayées au handle ni +ajoutées à `pending`. Le relais `term.onData` doit devenir actif seulement après le +callback de fin de `term.write(scrollback)` (ou être explicitement suspendu pendant ce +replay), tout en conservant le chemin nominal de frappe utilisateur ensuite. Test de +régression attendu dans `TerminalView.test.tsx` : un scrollback contenant des requêtes +terminal déclenche des réponses xterm simulées mais zéro `handle.write`; une saisie +`Hello` + Entrée après replay produit exactement les writes utilisateur attendus. + +Aucun correctif backend n'a été appliqué : filtrer les réponses terminal dans +`write_terminal` casserait le protocole PTY normal et masquerait l'origine frontend. +Aucun fichier des projets Unity n'a été modifié. + +## Validations DevBackend + +- `cargo test -p infrastructure pty --lib` — vert, 26 tests passés, 352 filtrés. +- `cargo test -p application --test agent_lifecycle launch_same_node_is_idempotent_no_respawn` — vert, 1 passé. +- `cargo test -p application --test agent_lifecycle launch_existing_agent_with_no_node_is_background_noop` — vert, 1 passé. +- `cargo test -p application --test orchestrator_service agent_run_visible_same_cell_rebinds_existing_session` — vert, 1 passé. +- `npm test -- --run src/features/terminals/TerminalView.test.tsx` depuis `frontend/` — vert, 21 tests existants ; la couverture actuelle ne simule pas les réponses xterm émises pendant le replay et doit être complétée par DevFrontend. + +Suite : DevFrontend implémente et teste la suspension du relais pendant le replay ; QA +rebuild l'AppImage puis reproduit sur SoundTool, UnityPlugin et IdeA, en contrôlant dans +`idea.log` qu'une saisie humaine ne produit qu'une seule séquence utilisateur et aucun +burst de réponses historiques à la réattache. Pour identifier le consommateur CPU hors +sandbox, QA peut utiliser `ps -eo pid,ppid,pcpu,comm,args --sort=-pcpu` puis corréler le +PID OpenCode/renderer avec l'heure du burst `pty-write`. + +## Correctif DevFrontend — 2026-09-09 + +Le relais xterm → PTY est désormais activé selon le cycle de montage : + +- ouverture fraîche : activation immédiate, afin de conserver le buffering des frappes + humaines pendant la résolution de l'opener ; +- réattache avec scrollback : aucun abonnement `term.onData` pendant + `term.write(scrollback)` ; activation seulement dans le callback de fin du replay, + puis adoption du handle ; +- réattache sans scrollback et fallback vers une ouverture fraîche : activation avant + adoption/ouverture selon le même invariant ; +- cleanup : disposal conditionnel de l'abonnement, puisqu'il peut ne pas encore exister + si la vue est démontée pendant le replay. + +Le test de non-régression simule une requête de position curseur (`ESC[6n`) dans le +scrollback et une réponse xterm synthétique (`ESC[1;1R`) émise par `term.write`. Il +vérifie zéro appel à `handle.write` pendant le replay, puis exactement un appel contenant +`Hello\r` après la fin du replay. + +Fichiers modifiés : + +- `frontend/src/features/terminals/TerminalView.tsx` +- `frontend/src/features/terminals/TerminalView.scrollback.test.tsx` +- `.ideai/tickets/292/carnet.md` + +Validations réelles : + +- `git diff --check` — vert. +- `npm --prefix frontend test -- --run src/features/terminals/TerminalView.scrollback.test.tsx src/features/terminals/TerminalView.portal.test.tsx src/features/terminals/TerminalView.test.tsx` — vert, 3 fichiers et 27 tests passés. jsdom signale seulement son avertissement connu `HTMLCanvasElement.getContext()` non implémenté. +- `npm --prefix frontend run build` — vert (`tsc --noEmit` puis Vite, 515 modules transformés). Seul l'avertissement existant de chunk supérieur à 500 kB reste présent. + +Reste à QA : rebuild de l'AppImage et reproduction hôte sur UnitySoundTool, +UnityPlugin et IdeA avec contrôle de l'absence de burst `pty-write` à la réattache. + +## Validation QA — 2026-09-09 + +Verdict QA : VERT sur le contrat automatisé et l'artefact AppImage. Limite explicite : +la reproduction hôte complète par interaction graphique dans les trois projets n'a pas +pu être conduite depuis ce sandbox QA sans envoyer des frappes à l'aveugle dans +l'application visible. + +Diff relu : + +- `frontend/src/features/terminals/TerminalView.tsx` : l'abonnement `term.onData` est + désormais créé par `activateInput()`. Sur réattache avec scrollback, `term.write` reçoit + un callback final qui active seulement ensuite le relais xterm -> PTY et adopte le + handle. Le chemin ouverture fraîche garde l'activation immédiate pour conserver le + buffering des frappes humaines. +- `frontend/src/features/terminals/TerminalView.scrollback.test.tsx` : le stub xterm + émet une réponse synthétique `ESC[1;1R` pendant le replay de `ESC[6n`; le test vérifie + zéro écriture PTY pendant ce replay, puis une seule écriture `Hello\r` après activation. + +Commandes réelles exécutées depuis `/home/anthony/Documents/Projects/IdeA` : + +```bash +git status --short --branch +``` + +```text +## fix/292-tui-native-git-agents-loop + M .ideai/tickets/counter.json + M .ideai/tickets/index.json + M frontend/src/features/terminals/TerminalView.scrollback.test.tsx + M frontend/src/features/terminals/TerminalView.tsx +?? .ideai/tickets/292/ +?? scripts/ +``` + +```bash +git diff --check +``` + +```text + +``` + +```bash +npm --prefix frontend test -- --run src/features/terminals/TerminalView.scrollback.test.tsx src/features/terminals/TerminalView.portal.test.tsx src/features/terminals/TerminalView.test.tsx +``` + +```text +Test Files 3 passed (3) +Tests 27 passed (27) +Duration 2.82s +``` + +La sortie contient aussi l'avertissement jsdom connu : +`Not implemented: HTMLCanvasElement's getContext() method: without installing the canvas npm package`. + +```bash +npm --prefix frontend run build +``` + +```text +tsc --noEmit && vite build +515 modules transformed. +dist/assets/index-Cce2ycJb.js 1,261.08 kB +✓ built in 2.60s +``` + +Vite signale seulement l'avertissement existant de chunk supérieur à 500 kB. + +Rebuild AppImage réel : + +```bash +cd crates/app-tauri +APPIMAGE_EXTRACT_AND_RUN=1 NO_STRIP=1 ../../frontend/node_modules/.bin/tauri build --bundles appimage +``` + +```text +Running beforeBuildCommand `npm --prefix ../frontend run build:bundle` +Finished `release` profile [optimized] target(s) in 2m 26s +Built application at: /home/anthony/Documents/Projects/IdeA/target/release/app-tauri +Bundling IdeA_0.3.0_amd64.AppImage (/home/anthony/Documents/Projects/IdeA/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage) +Finished 1 bundle at: + /home/anthony/Documents/Projects/IdeA/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage +``` + +Vérification d'artefact : + +```bash +stat -c '%n %s %Y %i' /home/anthony/Documents/Projects/IdeA/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage /home/anthony/Documents/Projects/IdeA/.appimage-build/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage +``` + +```text +/home/anthony/Documents/Projects/IdeA/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage 119478776 1788956753 12345211 +/home/anthony/Documents/Projects/IdeA/.appimage-build/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage 119478776 1788956753 12345211 +``` + +Les deux chemins sont le même artefact hardlinké, ce qui couvre les `opencode.json` des +agents Git affectés qui pointent vers `.appimage-build/.../IdeA_0.3.0_amd64.AppImage`. + +```bash +sha256sum /home/anthony/Documents/Projects/IdeA/.appimage-build/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage +``` + +```text +4165521e4248ec9f71af4ceb1c89d3aa54e8119a1ccf0f70d15268bdc67eaf94 /home/anthony/Documents/Projects/IdeA/.appimage-build/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage +``` + +Contrôles hôte tentés : + +```bash +env | sort | rg 'DISPLAY|WAYLAND|XDG_RUNTIME|IDEA|APPIMAGE|TAURI' +``` + +```text +APPIMAGE=/home/anthony/Documents/Projects/IdeA/.appimage-build/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage +DISPLAY=:0 +IDEA_MANAGED_TMP=/home/anthony/.local/share/app.idea.ide/managed-tmp/accb434c-d2d1-4c01-b928-72e738151fdc/data +QT_WAYLAND_RECONNECT=1 +WAYLAND_DISPLAY=wayland-0 +XDG_RUNTIME_DIR=/run/user/1000 +``` + +```bash +xdotool search --name 'IdeA - Agents' getwindowgeometry %@ +``` + +```text +Window 44040214 + Position: 0,28 (screen: 0) + Geometry: 1920x1052 +``` + +```bash +import -window 44040214 /home/anthony/.local/share/app.idea.ide/managed-tmp/accb434c-d2d1-4c01-b928-72e738151fdc/data/idea-agents-window-292.png +``` + +```text +import: missing an image filename `/home/anthony/.local/share/app.idea.ide/managed-tmp/accb434c-d2d1-4c01-b928-72e738151fdc/data/idea-agents-window-292.png' @ error/import.c/ImportImageCommand/1291. +``` + +```bash +xwd -id 44040214 -silent -out /home/anthony/.local/share/app.idea.ide/managed-tmp/accb434c-d2d1-4c01-b928-72e738151fdc/data/idea-agents-window-292.xwd && convert /home/anthony/.local/share/app.idea.ide/managed-tmp/accb434c-d2d1-4c01-b928-72e738151fdc/data/idea-agents-window-292.xwd /home/anthony/.local/share/app.idea.ide/managed-tmp/accb434c-d2d1-4c01-b928-72e738151fdc/data/idea-agents-window-292.png +``` + +```text +X Error of failed request: BadMatch (invalid parameter attributes) + Major opcode of failed request: 73 (X_GetImage) +``` + +```bash +APPIMAGE_EXTRACT_AND_RUN=1 /home/anthony/Documents/Projects/IdeA/.appimage-build/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage --version +``` + +```text +(app-tauri:12): Gdk-WARNING **: The program 'app-tauri' received an X Window System error. +The error was 'BadAccess (attempt to access private resource denied)'. +``` + +```bash +ps -eo pid,ppid,pcpu,pmem,comm,args --sort=-pcpu | head -40 +``` + +```text +PID PPID %CPU %MEM COMMAND COMMAND +1 0 20.0 0.0 codex codex-linux-sandbox ... +``` + +Le sandbox PID ne voit que ses propres processus ; il ne permet donc pas de mesurer +fiablement le CPU des processus hôtes IdeA/OpenCode. + +Contrôle log : + +```bash +tail -80 /home/anthony/.local/share/app.idea.ide/logs/idea.log +``` + +Le log actuel confirme surtout la signature pré-fix déjà collectée avant relance : +bursts `pty-write` de réponses synthétiques historiques (`\x1b[1;1R`, `\x1b[?1;2c`, +bulk writes) autour des sessions Git. Après le rebuild, aucun scénario graphique +nouveau n'a été exécuté depuis QA ; je ne conclus donc pas sur une absence live de burst +dans UnitySoundTool/UnityPlugin/IdeA. + +Conclusion QA : + +- VERT pour le correctif frontend ciblé : le test ajouté couvre la cause racine et + échouerait si `onData` restait actif pendant le replay. +- VERT pour les suites ciblées et le build frontend. +- VERT pour le rebuild AppImage avec `APPIMAGE_EXTRACT_AND_RUN=1 NO_STRIP=1`, artefact + disponible sur le chemin utilisé par les agents. +- Validation live hôte des trois Git TUI Native non conclue depuis ce sandbox faute de + pilotage UI fiable et de visibilité PID hôte. Une relance d'IdeA sur l'AppImage + rebuildée puis une saisie manuelle `Hello` dans IdeA, UnitySoundTool et UnityPlugin + reste le seul complément non automatisé. diff --git a/.ideai/tickets/292/issue.md b/.ideai/tickets/292/issue.md new file mode 100644 index 00000000..fa00cdc3 --- /dev/null +++ b/.ideai/tickets/292/issue.md @@ -0,0 +1,17 @@ +--- +id: "0889ead8-ddde-4655-a5cb-2a66531c3786" +number: 292 +title: "Agents Git TUI Native bouclent sans réponse dans UnitySoundTool et UnityPlugin" +status: "qa" +priority: "high" +sprint: null +links: [] +agentRefs: [{"agentId":"ab328d90-c307-4771-a3b6-6c56089c8506","role":"assigned"}] +attachments: [] +createdBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} +updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} +createdAt: 1788955001961 +updatedAt: 1788956518862 +version: 5 +--- +Symptôme rapporté le 2026-09-09 : dans les projets UnitySoundTool et UnityPlugin, l'agent Git lancé en TUI Native avec une commande simple comme "Hello" boucle, ne répond jamais et fait monter le CPU à 100%. L'agent Git du projet IdeA actuel, configuré de manière apparemment identique, fonctionne correctement. Investigation attendue : comparer les manifests/profils/runtime TUI Native de ces projets, identifier la différence effective, puis corriger côté runtime/config si IdeA provoque une boucle de lancement ou d'entrée PTY. \ No newline at end of file diff --git a/.ideai/tickets/counter.json b/.ideai/tickets/counter.json index fd4fdce1..c631140c 100644 --- a/.ideai/tickets/counter.json +++ b/.ideai/tickets/counter.json @@ -1,3 +1,3 @@ { - "nextNumber": 292 + "nextNumber": 293 } \ No newline at end of file diff --git a/.ideai/tickets/index.json b/.ideai/tickets/index.json index 85b029ea..3b79826f 100644 --- a/.ideai/tickets/index.json +++ b/.ideai/tickets/index.json @@ -3993,6 +3993,22 @@ "agent_id": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d" }, "updatedAt": 1788940935288 + }, + { + "issueRef": "#292", + "path": "292", + "title": "Agents Git TUI Native bouclent sans réponse dans UnitySoundTool et UnityPlugin", + "status": "qa", + "priority": "high", + "sprint": null, + "assignedAgentIds": [ + "ab328d90-c307-4771-a3b6-6c56089c8506" + ], + "createdBy": { + "kind": "agent", + "agent_id": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d" + }, + "updatedAt": 1788956518862 } ] } \ No newline at end of file