199 Commits

Author SHA1 Message Date
17d6baf15a Merge feature/ticket77-paired-devices into develop
Intègre le ticket #77 : l'appairage repose désormais sur des appareils
persistants, nommés et révocables, derrière un code éphémère à usage
unique (TTL 10 min) au lieu d'un code permanent imprimé sur stdout.

Ferme aussi la dette #76 : la normalisation du code passe côté serveur,
là où #75 ne pouvait que la masquer depuis le frontend.

Suites rejouées avant merge, toutes vertes :
- cargo test -p domain -p application -p infrastructure -p web-server :
  exit 0, 89 suites, 1686 tests.
- frontend : 91 fichiers, 848 tests, tsc exit 0.
- garde-fou de bundle : dist = tauri, dist-web = http.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 13:27:17 +02:00
1be5f8cd18 chore(ideai): #77 en QA, ouverture de #78 et #79
Enregistre l'état du registre de tickets : #77 entre en QA, #78 ouvre la
dette UX de langue de l'écran Settings et #79 le test flaky relevé en
cours de route.

État runtime uniquement : aucun code de feature n'est touché.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 13:27:06 +02:00
3f1e132e88 feat(frontend): écran Appareils et parcours d'appairage nommé (#77 F1/F2)
Expose la gestion des appareils appairés introduite en B1-B4, sur une
surface unique partagée par le web et le desktop.

- Écran Appareils : liste, renommage, révocation unitaire ou globale,
  activité formatée, panneau de code éphémère.
- Le parcours d'appairage demande un nom d'appareil, pour qu'une
  révocation porte sur quelque chose d'identifiable par l'utilisateur.
- Gateways DeviceGateway en trois adapters (Tauri, HTTP, Mock), le port
  restant le seul contrat connu de la feature.

Les erreurs sont mappées localement et le message du serveur n'est jamais
affiché tel quel : un échec d'appairage ne doit pas devenir un oracle pour
qui teste des codes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 13:27:06 +02:00
8fe93d1652 feat(backend): appareils appairés persistants, révocables et code éphémère (#77 B1-B4)
L'appairage ne survivait pas au redémarrage et son code, permanent, était
imprimé sur la sortie standard. Un appareil appairé devient une entité
persistante, nommée et révocable, derrière un code désormais éphémère.

- B1 : port DeviceSessionStore et adapter FsDeviceSessionStore, entités de
  domaine (PairedDevice, DeviceId, SessionTokenHash, DeviceName). Les tokens
  sont hachés en SHA-256 et comparés en temps constant (subtle) : le store
  ne peut pas rejouer une session qu'il a servie. Cookie Max-Age 400 j à
  renouvellement glissant, lastSeenAtMs throttlé.
- B2 : code éphémère en mémoire, TTL 10 min et usage unique, toute
  génération invalidant la précédente. POST /api/pairing-code authentifiée,
  flag --new-code. Le code est retiré du boot et l'eprintln! qui l'imprimait
  est supprimé.
- B3 : endpoints devices (list/rename/revoke/revoke-all/logout), event
  DeviceRevoked et ActiveConnectionRegistry par device_id, fermant sans
  délai les WebSockets d'un appareil révoqué.
- B4 : port PairAttemptLimiter et adapter mémoire, rate-limit par origine et
  global sur horloge injectée, donc testable sans attente réelle.

La normalisation du code passe côté serveur : elle absorbe la dette #76, que
la seule normalisation frontend de #75 ne faisait que masquer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 13:26:56 +02:00
9463b7e6bf Merge feature/ticket75-pairing-code-keyboard into develop
Intègre le ticket #75 : l'écran d'appairage web appelait le pavé
numérique des mobiles alors que le code d'appairage est de
l'hexadécimal majuscule, rendant les lettres A-F non saisissables.

Le champ passe en clavier texte, annonce le format réel et normalise
la saisie avant envoi. Le correctif revient sur le choix de #69, qui
avait supposé un code purement numérique.

Suite verte rejouée avant merge : 88 fichiers, 801 tests, tsc exit 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 10:41:01 +02:00
481a7e2498 chore(ideai): clôture de #74 et #68, ouverture de #75 et #76
Enregistre l'état du registre de tickets : #74 et #68 passent en closed
après la validation live de l'intégration web, #75 (ce fix) entre en QA
et #76 ouvre la dette de casse du code d'appairage côté serveur.

État runtime uniquement : aucun code de feature n'est touché.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 10:40:26 +02:00
120d0d1ac2 fix(frontend): clavier texte et normalisation du code d'appairage (#75)
Le code d'appairage est de l'hexadécimal majuscule (p. ex. 3F7A9C21),
pas une suite de chiffres. Le champ appelait pourtant le pavé numérique
des mobiles, rendant les lettres A-F non saisissables.

Le champ passe donc en inputMode="text" avec autoCapitalize="characters",
ce qui revient sur le choix inverse fait en #69 : ce ticket avait supposé
un code purement numérique. Le placeholder et l'aide du champ décrivent
désormais le format réel.

La saisie est normalisée (majuscules, espaces retirés) avant pair() car
le serveur compare le code strictement ; la dette correspondante côté
backend est suivie en #76.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 10:40:26 +02:00
aec4293750 Merge feature/ticket74-web-transport-bundle into develop
Intègre les tickets #68 (packaging des assets web en ressource Tauri) et
#74 (le serveur embarqué servait le bundle desktop au navigateur).

Le frontend produit désormais deux artefacts Vite distincts : dist
(transport tauri, build.frontendDist) et dist-web (transport http,
packagé en ressource « web/ »). Le garde-fou de build échoue si un
bundle résout le mauvais transport.

Les deux tickets ferment ensemble : le packaging #68 servait le bundle
cassé, il ne pouvait pas être déclaré vert sans le fix #74. Validation
live utilisateur verte derrière reverse proxy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 10:34:32 +02:00
4de25d2bce chore(ideai): état runtime du ticket #74 (passage en QA)
Enregistre le passage de #74 en statut QA (issue, carnet, index) et
l'accusé de complétion des rendez-vous headless du lot.

État runtime uniquement : aucun code de feature n'est touché.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 10:34:24 +02:00
f605989fa5 chore(ideai): note mémoire du seam de transport des bundles (#74)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 09:09:46 +02:00
25728dee74 test(frontend): garde-fou sur le transport des bundles construits (#74 Q1)
Vérifie sur les artefacts réels que `dist` résout `tauri` et `dist-web`
`http`, en lisant le marqueur `__IDEA_TRANSPORT__` des assets JS émis. La
régression de #74 était invisible aux tests unitaires : elle vivait dans la
conf de build et de packaging, pas dans le code testé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 09:09:42 +02:00
5ca2ab77f3 fix(app-tauri): packager le bundle web, pas le bundle desktop (#74 B1)
`bundle.resources` packageait `frontend/dist` — le bundle desktop — sous
`web/`, que `resolve_web_root()` sert au navigateur. La ressource pointe
désormais sur `frontend/dist-web` (transport http), et `beforeBuildCommand`
appelle `build:bundle` pour que les deux bundles existent au packaging.

`frontendDist` reste sur `dist` : le desktop est inchangé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 09:09:42 +02:00
a7197fc53b feat(frontend): produire un bundle web distinct en transport http (#74 F1)
Le transport est figé au build par Vite : un seul `dist` ne peut pas servir
à la fois le desktop (IPC Tauri) et le navigateur (HTTP+WS). Le serveur
embarqué servait donc un bundle desktop, d'où `__TAURI_INTERNALS__ is
undefined` côté navigateur.

- `vite.config.ts` devient une factory `({ mode })` : le mode par défaut émet
  le bundle desktop (`dist`), `--mode web` lit `.env.web` et émet le bundle
  navigateur (`dist-web`).
- `.env.web` porte `VITE_TRANSPORT=http` dans un fichier de mode plutôt qu'en
  préfixe de commande, syntaxe qui n'existe pas sous Windows (bundle NSIS).
- `transport.ts` extrait le prédicat `transportFromEnv()`, partagé par l'app et
  la config de build : le constant `__IDEA_TRANSPORT__` et le transport résolu
  dérivent de la même variable via le même prédicat, ils ne peuvent pas diverger.
- `main.tsx` publie `__IDEA_TRANSPORT__` sur `window` : l'affectation est un
  effet de bord, elle survit à la minification et rend un bundle identifiable
  sans grep d'un symbole minifié. Les deux jeux d'adapters étant présents dans
  les deux bundles, leur présence ne prouve rien.

Le chemin desktop est inchangé : le web reste opt-in.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 09:09:33 +02:00
50d237e836 chore(ideai): ouverture du ticket #74 (bundle desktop servi au navigateur)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 08:54:19 +02:00
166adc3c4b feat(app-tauri): packager les assets web en ressource et les résoudre depuis le resource dir (#68)
L'AppImage ne pouvait pas servir d'assets web : `resolve_web_root` ne
connaissait que `IDEA_WEB_ROOT`, le dossier de l'exécutable et le cwd —
aucun ne pointe vers un bundle packagé.

- `bundle.resources` embarque les assets sous `web/`, `beforeBuildCommand`
  déclenche le build du frontend.
- Le resource dir Tauri descend de `lib.rs` jusqu'à `EmbeddedServerController`
  via `AppState::build_with_resource_dir`, en gardant les constructeurs
  historiques comme façade.
- `resolve_web_root` insère le candidat packagé juste après `IDEA_WEB_ROOT`,
  qui garde donc la priorité pour le développement.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 08:54:15 +02:00
a13a6c1801 chore(ideai): carnet de #68, passage en QA et note du faux vert sandbox
Dernier état `.ideai/` du sprint serveur embarqué, séparé du code applicatif
conformément aux précédents `f965f64` / `7fbaa8e` / `5ee25d1`.

- #68 passé en `qa`, pas en `closed` : mergé dans `develop` (`b30c9c7`) et vert,
  mais RIEN n'a été cliqué dans l'app réelle. « Mergé » ne veut pas dire
  « validé » — même distinction que pour #69 et son lot 3.
- Carnet de #68 : périmètre livré, réserve « aucune validation live », et la
  dette identifiée (ordre de `preview_settings` qui verrouille la liste des
  candidats LAN, warning `missingTrustedProxy` inatteignable).
- Mémoire projet : note `sandbox-eperm-bind-false-green-web-server`. Elle
  capitalise le piège qui s'est refermé DEUX FOIS le 2026-07-16 : les sandboxes
  de DevBackend et QA refusent `TcpListener::bind`, donc tout `cargo test` sur
  `web-server`/`app-tauri` y rend un vert qui ne prouve rien. La première fois,
  ce faux vert masquait un vrai bug produit — un serveur embarqué sur port
  éphémère rejetait toutes les requêtes API en 403. La règle qui en sort : aucun
  repli EPERM silencieux, et tout vert sur ces crates doit être produit hors
  sandbox.
- Journal des tâches de fond : complétions enregistrées.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 06:03:11 +02:00
f965f64b07 chore(ideai): clôture de #72 (confiance reverse proxy)
#72 passé en `closed` : mergé dans `develop` via `ae01297`, vert hors sandbox,
branche supprimée. Carnet complété avec le périmètre livré et les arbitrages.

État `.ideai/` indépendant du code de #68, committé directement sur `develop`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 23:44:12 +02:00
b30c9c7590 Merge feature/ticket68-embedded-server-desktop into develop (#68)
Ferme #68 : le serveur web s'active désormais depuis l'app desktop, via un
panneau Settings > Deployment. C'est la fonctionnalité demandée à l'origine du
ticket, et l'aboutissement de la chaîne #65 (extraction d'idea-serve) → #68 B1
(seam shared-core) → #72 (durcissement proxy) → ce lot.

Backend : `EmbeddedServerController` sur `AppState`, commandes start/stop/status,
store `<app-data-dir>/deployment/server-exposure.json` (config de sécurité, pas
préférence d'UI), arrêt câblé sur la sortie d'app. Le serveur embarqué consomme
`run_embedded_with_core` avec le `BackendCore` du desktop — une seule
composition root, ce pour quoi le seam de B1 existait.

Frontend : `features/settings/` avec `SettingsView`/`DeploymentSettings`,
gateway `DesktopServerGateway`, et `ProjectsView` dont le booléen `showSettings`
devient une vraie navigation `AI Profiles` / `Deployment`.

Vert avant merge, ré-exécuté par Git HORS SANDBOX — impératif sur ces crates,
le sandbox bloque `bind` et a déjà produit un faux vert sur #68 B1 :
`cargo test -p app-tauri` 43 passed / 1 ignored · `-p web-server` 66 passed ·
`npm run typecheck` exit 0 · `npx vitest run` 87 files / 789 passed. Le test
ignoré est une garde d'environnement préexistante, vérifiée passante hors
sandbox avec `--ignored`.

Invariants vérifiés par Git avant merge : `run_embedded_with_core` bien
l'appelant, aucune fuite des types d'exposition dans `domain`/`application`,
`0600` posé avant le `rename`, `stop()` appelé à la fermeture.

Dette consignée, non bloquante : `preview_settings` valide avant de
prévisualiser (verrouille la liste des candidats LAN, contourné côté frontend),
et le warning `missingTrustedProxy` est inatteignable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 23:43:56 +02:00
7efa634f23 feat(frontend): panneau Settings Deployment et gateway serveur desktop (#68 F1+F2)
Donne à l'utilisateur la surface pour activer le serveur depuis l'app.

- `features/settings/` : `SettingsView`, `DeploymentSettings`, `useDeployment`.
- `adapters/desktopServer.ts` + port `DesktopServerGateway` et DTO associés ;
  adapters mock et http/unsupported alignés (le mode web n'expose pas le
  contrôle du serveur qui l'héberge).
- `ProjectsView` : le `showSettings: boolean` devient une navigation interne
  `AI Profiles` / `Deployment`. Le libellé alternant « Close AI Profiles »
  disparaît — Settings existait déjà dans cette vue, la surface évolue au lieu
  d'ajouter un `PanelId`.

CORRECTION D'UN BRIEF FAUX, remontée spontanément par DevFrontend et qui mérite
de survivre : le cadrage décrivait le mode `remoteProxyOtherMachine` avec deux
champs. `validate_settings` en exige un troisième, `lanBindAddress`, et rejette
loopback comme unspecified. Construit selon la spec, chaque save et chaque start
en mode 3 aurait échoué — le lot serait parti vert et cassé. Le panneau expose
donc un select alimenté par `candidateLanAddresses` fourni par le backend :
la règle « le frontend n'invente jamais une IP » tient.

QA ré-exécutée par Git avant merge : `npm run typecheck` exit 0 ·
`npx vitest run` 87 files / 789 passed, 0 échec.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 23:43:37 +02:00
49e1d5a157 feat(app-tauri): cycle de vie du serveur embarqué et persistance de l'exposition (#68 B2)
Permet d'activer le serveur web depuis l'app desktop — la fonctionnalité
demandée à l'origine du ticket.

- `embedded_server.rs` : `EmbeddedServerController` porté par `AppState`, avec
  les commandes `embedded_server_start` / `stop` / `status` et
  `get` / `save` / `preview_server_exposure_settings`.
- `FsServerExposureSettingsStore` : écrit `<app-data-dir>/deployment/server-exposure.json`.
  C'est de la config de sécurité, pas une préférence d'UI — d'où l'app-data-dir
  global plutôt que `localStorage`. Écriture atomique : temporaire, `0600` posé
  AVANT le `rename`, donc le fichier final n'est jamais brièvement lisible par
  tous.
- Arrêt du serveur câblé sur la sortie de l'app (`lib.rs`), aux côtés des autres
  nettoyages.

Le serveur embarqué appelle `run_embedded_with_core` avec le `BackendCore`
partagé du desktop, jamais `run_embedded` — c'est tout l'intérêt du seam ouvert
par B1 : une seule composition root dans le processus, pas deux.

DETTE CONNUE, consignée au carnet, non bloquante :
- `preview_settings` valide avant de prévisualiser (`embedded_server.rs:211`),
  ce qui verrouille la liste des candidats : un brouillon en mode
  `remoteProxyOtherMachine` ne peut pas être prévisualisé tant qu'il n'a pas le
  `lanBindAddress` que cette liste doit justement fournir. Contourné côté
  frontend par une sonde `localOnly` toujours valide. Défaut d'ordonnancement,
  pas une fatalité.
- Le warning `missingTrustedProxy` (`embedded_server.rs:452`) est du code mort :
  `validate_settings` rejette les `trustedProxies` vides en mode 3 avant que
  `preview_settings` ne puisse le produire.

QA ré-exécutée par Git HORS SANDBOX (DevBackend et QA sont bloqués par EPERM sur
`bind` ; un vert sandboxé ne prouve rien sur ces crates) : `cargo test -p
app-tauri` 43 passed, 1 ignored · `-p web-server` 66 passed, 0 échec.
Le test ignoré `mcp_bridge::tests::end_to_end_over_real_loopback` est
PRÉEXISTANT sur `develop` et non touché par ce lot ; lancé explicitement hors
sandbox avec `--ignored`, il passe. C'est une garde d'environnement, pas un faux
vert — distinction qui nous a déjà coûté deux fois aujourd'hui.

Invariants vérifiés par Git : appelant `run_embedded_with_core` confirmé, aucune
fuite d'`EmbeddedServer*`/`ServerExposure*` dans `domain` ni `application`,
ordre chmod/rename correct, `stop()` bien appelé à la fermeture.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 23:43:24 +02:00
7fbaa8eba6 chore(ideai): ouverture de #73 (TLS intégré à idea-serve)
#73 ouvert en priorité haute : intégrer TLS à `idea-serve` pour supprimer la
cause racine de la cérémonie reverse proxy, dont #72 vient de durcir les
garde-fous. Lié à #72, #66, #68 et #71.

État `.ideai/` indépendant du code de #72, committé directement sur `develop`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 23:11:15 +02:00
ae01297b6b Merge feature/ticket72-proxy-trust-hardening into develop (#72)
Rend réelle la confiance accordée au reverse proxy. Jusqu'ici
`--trust-reverse-proxy` n'était lu que pour exiger sa propre présence : le
serveur ne vérifiait ni qui se connectait, ni ce que le proxy annonçait. Le pair
TCP est désormais vérifié avant toute confiance aux headers, `X-Forwarded-Proto:
https` est exigé en mode proxy, et `X-Forwarded-For` n'autorise jamais rien.

Corrige aussi B0, le pendant CLI du bug de port de #68 B1 : `idea-serve --listen
127.0.0.1:0` ne compare plus l'origine à `http://127.0.0.1:0`.

CHANGEMENT DE COMPORTEMENT ASSUMÉ : un bind non-loopback distant sans
`--trusted-proxy` est refusé au démarrage. Les configurations existantes de cette
forme cassent volontairement, avec un message qui dit quoi ajouter.

Vert avant merge, ré-exécuté par Git HORS SANDBOX — impératif sur ce crate, le
sandbox interdit `TcpListener::bind` et a déjà produit un faux vert sur #68 B1 :
`cargo test -p web-server` 66 passed · `-p backend` · `-p app-tauri` verts.
Revue de sécurité : guard câblé sur les 3 surfaces (statique, /api/*, /api/ws),
vrai `peer_addr` propagé sur tout le chemin de production.

Étape 2 du plan d'intégration séquentiel. Suit : #68 (panneau Settings
Deployment), sur arbitrage utilisateur qui a refusé de le reléguer derrière #71
et #73.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 23:11:04 +02:00
42804fc55d docs(server): documenter --trusted-proxy et l'exigence X-Forwarded-Proto (#72)
Aligne la doc du mode distant sur le durcissement de #72 : exemple complet du
cas proxy sur une autre machine avec `--trusted-proxy`, mention que tout bind
non-loopback exige au moins un proxy autorisé, et obligation faite au proxy
d'envoyer `X-Forwarded-Proto: https`.

Précise que transmettre le host public est recommandé pour le diagnostic mais
n'est pas la vérification d'accès : celle-ci reste l'égalité stricte d'`Origin`
contre `--public-origin`.

Bascule les IP d'exemple vers la plage de documentation RFC 5737 (`192.0.2.0/24`)
au lieu d'un réseau privé plausible.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 23:10:47 +02:00
75d1ce87fc feat(web-server): donner un effet réel à la confiance reverse proxy + fix port CLI (#72)
`--trust-reverse-proxy` était un drapeau creux : il n'était lu que dans
`ServerConfig::validate()` pour exiger sa propre présence. Le serveur n'a jamais
vérifié qui se connectait ni ce que le proxy annonçait. Ce lot lui donne un
effet.

- `trusted_proxies: Vec<TrustedProxy>` dans `ServerConfig` + `--trusted-proxy`
  (répétable, IP ou CIDR v4/v6). `validate()` refuse désormais au démarrage un
  bind non-loopback en mode remote sans au moins un proxy autorisé.
- Guard runtime sur les trois surfaces (statique, `/api/*`, `/api/ws`) : le
  `peer_addr` réel est vérifié AVANT toute confiance accordée aux headers. Sur
  bind loopback, seul un pair loopback est accepté ; sinon le pair doit tomber
  dans un `--trusted-proxy`.
- `X-Forwarded-Proto: https` obligatoire en mode proxy, avec un message de refus
  actionnable (la commande nginx exacte à ajouter).
- `X-Forwarded-For` n'autorise jamais rien : il n'est que journalisé. Un header
  ne donne aucun droit, seul le pair TCP en donne.
- Diagnostics : `UntrustedProxyPeer`, `ForwardedProtoRejected`,
  `ForwardedHostMismatch`.

B0 — même bug de port que celui corrigé dans #68 B1, sur le chemin CLI cette
fois : `run_server` construisait son `ServerState` AVANT le bind, donc
`idea-serve --listen 127.0.0.1:0` comparait l'origine à `http://127.0.0.1:0` et
rejetait tout en 403. La réconciliation du port effectif est factorisée dans
`config_with_effective_listen`, partagée avec le chemin embarqué.

`X-Forwarded-Host` ne provoque PAS de rejet : Architect l'a jugé redondant avec
la vérification stricte d'`Origin`, et il cassait nginx en configuration par
défaut. Il reste un warning de diagnostic. La vérification d'accès stricte
demeure l'égalité d'`Origin` contre `--public-origin`.

CHANGEMENT DE COMPORTEMENT ASSUMÉ — ce lot casse volontairement les
configurations existantes : un bind non-loopback distant sans `--trusted-proxy`
est désormais refusé au démarrage. C'est le prix d'un drapeau qui ne mentait
plus. Le message d'erreur dit quoi ajouter.

Origine du ticket : #72 est né d'une trouvaille de l'agent Git à la revue de la
doc de #65 — c'est en vérifiant une phrase de sécurité qu'il a établi que
`--trust-reverse-proxy` n'avait aucun effet runtime.

QA ré-exécutée par Git HORS SANDBOX avant merge (DevBackend et QA sont tous deux
bloqués par EPERM sur `TcpListener::bind` ; un vert sandboxé ne vaut rien ici, on
s'est déjà fait avoir sur #68 B1) : `cargo test -p web-server` 66 passed, 0
échec · `-p backend` · `-p app-tauri` verts.

Revue de sécurité par Git : guard câblé sur les 3 routes, chemin de production
propageant toujours le vrai `peer_addr` (le repli `listen.ip()` est
`#[cfg(test)]`, inatteignable en production), CIDR correct y compris `prefix 0`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 23:10:40 +02:00
e8a72f5173 Merge docs/remote-mode-proxy-clarification into develop (#65)
Corrige une documentation qui a réellement induit l'utilisateur en erreur :
l'exemple du cas « Remote HTTPS » bindait `127.0.0.1` en supposant sans le dire
que le reverse proxy est co-localisé. Suivi tel quel avec un proxy sur une autre
machine, il produit un serveur injoignable.

Précise le cas co-localisé vs le cas distant, la configuration exigée pour tout
bind non-loopback, l'égalité stricte de `--public-origin` contre l'`Origin`
(ouvrir l'UI par le domaine, pas par l'IP, sinon 403), et le pare-feu hôte comme
cause de timeout depuis les autres machines.

Conserve l'obligation normative « ne pas exposer sans proxy TLS » que la
première rédaction avait perdue au profit d'une garantie que le code n'offre
pas : `ServerConfig::validate()` valide la configuration, pas la présence réelle
d'un proxy.

Indépendant de #68 et #71 : n'a attendu aucun de leurs lots.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 18:51:23 +02:00
677f64d18e docs(server): restaurer l'obligation de proxy TLS, que validate() n'assure pas (#65)
La réécriture précédente avait remplacé « Do not expose the backend on a public
non-loopback address without TLS proxying » par une formulation adossée à
`ServerConfig::validate()`. Littéralement vraie, mais pas équivalente : elle
troque une OBLIGATION faite à l'opérateur contre une garantie automatique qui
n'existe pas.

`validate()` valide une configuration, pas la réalité. Rien ne vérifie qu'un
proxy est réellement devant le serveur : passer les trois drapeaux en bindant
`0.0.0.0` sans aucun proxy satisfait `validate()` et sert du HTTP en clair.
La phrase normative est donc rétablie, avec cette limite dite explicitement.

Voir #72 : `--trust-reverse-proxy` n'a par ailleurs aucun effet à l'exécution.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 18:51:11 +02:00
f915635e07 chore(ideai): ouverture de #72 (confiance reverse proxy sans effet réel)
- #72 ouvert en priorité haute, bloquant #68 : `--trust-reverse-proxy` n'est lu
  que dans `ServerConfig::validate()` pour exiger sa propre présence — le serveur
  ne parse jamais `X-Forwarded-*`. C'est un drapeau d'intention pur, qui donne un
  faux sentiment de protection. Le ticket porte aussi B0 (le bug de port effectif
  sur le chemin CLI `run_server`), `trusted_proxies` + `--trusted-proxy`, le
  guard runtime et les événements de diagnostic.
- Journal des tâches de fond : complétions enregistrées.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 18:50:57 +02:00
7c677c4fb4 docs(contexte): ajouter le rôle UX au contexte projet et au cycle obligatoire
Déclare UX comme propriétaire de la conception des surfaces (navigation,
libellés, hiérarchie de l'information, formulation des erreurs, parcours) et
l'insère dans le cycle : avant Architect quand la forme conditionne les contrats,
avec lui quand une contrainte technique borne la conception. UX ne bloque pas les
lots sans surface utilisateur.

Ajoute aussi l'avertissement que la liste des rôles de ce document n'est pas la
source de vérité des agents déclarés : `idea_list_agents` fait autorité.

Contexte projet, indépendant des chantiers en cours — committé directement sur
`develop`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 18:50:50 +02:00
328c941f22 Merge feature/ticket68-embedded-server-core into develop (#68 B1)
Livre la fondation backend du serveur embarqué desktop : le seam
`run_embedded_with_core(config, Arc<BackendCore>)`, qui permet à l'app Tauri de
faire tourner le serveur web en partageant son composition root plutôt qu'en
dupliquant un backend dans le même processus.

Apporte aussi le fix du port effectif : un serveur embarqué sur port éphémère
rejetait toutes les requêtes API en 403 (origine comparée à un `listen` resté à
port 0). Et comble le trou de couverture sur `run_embedded().stop()` signalé au
merge de #65.

Vert avant merge, ré-exécuté par Git HORS SANDBOX — impératif ici : le sandbox
interdit `TcpListener::bind` et avait produit un faux vert sur ce lot précis.
`cargo test -p web-server` 57 passed (les 2 tests embedded réellement exécutés) ·
`-p backend` 41 · `-p app-tauri` 35, 0 échec.

Étape 1 du plan d'intégration séquentiel. Suit : #72 (durcissement bind/origine/
proxy, dont B0 = le même bug de port sur le chemin CLI `run_server`), puis #71
lot 1 (diagnostics), puis le Settings Deployment de #68.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 18:50:37 +02:00
b7d38f1d30 feat(web-server): seam shared-core run_embedded_with_core + fix du port effectif (#68 B1)
Ouvre le point d'intégration du serveur embarqué desktop : `run_embedded_with_core`
reçoit un `Arc<BackendCore>` déjà construit, pour que l'adapter HTTP partage le
composition root de l'adapter Tauri au lieu d'en bâtir un second dans le même
processus. `ServerState` porte désormais `Arc<BackendCore>` ; `run_embedded` est
conservé en compat et délègue en construisant son propre core.

Corrige au passage un vrai bug produit, découvert en rejouant les tests hors
sandbox : `run_embedded` ne réconciliait pas le port effectif avec la config. Sur
un bind éphémère (`127.0.0.1:0`), l'état du serveur gardait `listen` à port 0,
donc `origin_allowed` comparait l'Origin entrante à `http://127.0.0.1:0` et le
serveur embarqué rejetait **toutes** les requêtes API en 403. La config reçoit
maintenant `local_addr` lu sur le listener **avant** la construction du state.

Comble le trou signalé au merge de #65 : `run_embedded().stop()` est enfin
couvert.

ATTENTION — le premier « 57 passed » de ce lot était un FAUX VERT. Le sandbox
d'exécution interdit `TcpListener::bind` ; les tests sortaient en silence sur
EPERM, dont précisément le test `stop()`. Les replis EPERM sont supprimés : un
bind refusé fait désormais échouer le test au lieu de le peindre en vert.

QA ré-exécutée par Git HORS SANDBOX avant merge (sinon on reproduit le faux
vert) : `cargo test -p web-server` 57 passed, 0 échec, avec
`run_embedded_stop_shuts_down_accept_loop` et
`run_embedded_with_core_uses_injected_core_for_http_invokes` réellement exécutés
sur le vrai chemin réseau. `-p backend` 41 · `-p app-tauri` 35, 0 échec.

DETTE CONNUE, tracée en #72 B0 : le chemin CLI standalone `run_server` garde la
même hypothèse fausse — il reçoit un `ServerState` construit AVANT le bind, donc
`idea-serve --listen 127.0.0.1:0` reste cassé à l'identique.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 18:50:18 +02:00
77684ea183 docs(server): clarifier le bind du cas proxy distant et l'origine stricte (#65)
L'exemple du cas « Remote HTTPS » bindait `127.0.0.1` en supposant sans le dire
que le reverse proxy tourne sur la même machine que le serveur. Suivi tel quel
avec un proxy sur une autre machine, il produit un serveur injoignable — ce qui
a réellement induit l'utilisateur en erreur aujourd'hui.

Précise donc : le cas co-localisé vs le cas proxy distant (binder une adresse
joignable par le proxy, lien proxy→serveur en HTTP clair sur le LAN), la
configuration exigée pour tout bind non-loopback, l'égalité stricte de
`--public-origin` contre l'`Origin` de la requête (ouvrir l'UI par le domaine et
non par l'IP, sinon 403), et le pare-feu hôte comme cause de timeout depuis les
autres machines.

Indépendant de #68 et #71 : valeur immédiate, n'attend aucun de leurs lots.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 18:11:56 +02:00
56b9f5f619 chore(ideai): clôture de #65 et ouverture de #71 (diagnostic d'accessibilité)
État `.ideai/` indépendant des branches de feature, committé directement sur
`develop` (précédents `2fa226e`, `56757c7`).

- #65 « serveur headless idea-serve » passé en `closed` par Main : mergé dans
  `develop` via `fe0e53e`, vert, branche supprimée.
- #71 « diagnostic d'accessibilité du serveur » ouvert, lié à #68 et #65.
- Journal des tâches de fond : complétions enregistrées.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 18:11:38 +02:00
32c670a2b3 Merge feature/ticket69-mobile-responsive-client into develop (#69)
Rend le client web utilisable sur téléphone : `100dvh` + `viewport-fit=cover` et
safe-area insets (la barre d'URL rétractable masquait le bas), rangées qui
s'empilent sous 360px, et surtout une `TerminalKeyBar` (Esc/Tab/Ctrl-C/Ctrl-D/
flèches) qui rend le terminal pilotable au doigt — le clavier virtuel n'offre
aucune de ces touches, on pouvait donc parler à un agent CLI mais pas
l'interrompre.

Surface frontend-pure : aucun DTO, use case ni `LayoutTree` touché. Le chemin
desktop est inchangé (`LayoutGrid` ne passe pas `onReady`, `onReady` est
optionnel).

Vert avant merge, ré-exécuté par Git sur la base rebasée (les chiffres du carnet
ne valaient que sur `506d589`) : `npm run typecheck` exit 0 · `npx vitest run`
85 files / 774 passed, 0 échec · `VITE_TRANSPORT=http npx vite build` exit 0.

RÉSERVE — le lot 3 est livré mais NON PROUVÉ en conditions réelles.
La validation live utilisateur du 2026-07-16 (téléphone réel, via le reverse
proxy) est PARTIELLE. Couvert : le terminal s'ouvre, ce qui implique appairage +
workspace traversés, donc les lots 1 et 2 (`100dvh`, `60dvh`) validés en réel.
NON couvert : la `TerminalKeyBar` n'a pas été utilisée, et l'invariant de focus
au tap (« chaque tap annule le déplacement de focus puis refocalise xterm », sans
lequel le clavier virtuel se referme à chaque touche) n'a pas été observé sur
navigateur mobile réel. C'est le cœur du ticket. Le test manquant tient en 30 s :
lancer une commande longue, taper Ctrl-C dans la barre, vérifier que la commande
s'interrompt ET que le clavier reste ouvert.
Merge autorisé par Main sur ce constat : risque résiduel borné (frontend-pur,
desktop non atteint, non-régression prouvée). Si le test Ctrl-C échoue, il ouvre
un bug, il ne réverte pas ce merge. Jusque-là, « mergé » ne veut pas dire
« validé ».

Trou de couverture connu et assumé : jsdom n'évalue pas les media queries, les
tests sont structurels et ne prouvent ni les breakpoints, ni `dvh`, ni les safe
areas, ni le clavier virtuel. La décision d'ajouter un vrai navigateur
(Playwright) appartient à #63 « Système de test de l'UI ».

Rebase préalable sur `develop@fe0e53e` (#65 avait fait avancer la base) :
sans conflit, aucun recouvrement de fichiers, arbre `frontend/` identique bit
pour bit à l'original `6ed0087`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 15:16:21 +02:00
24f5e85974 chore(ideai): carnet de #69 — validation live partielle et réserve du lot 3
État `.ideai/` du chantier client téléphone, séparé du code applicatif
conformément aux précédents `5ee25d1` / `8e481ae`.

- Carnet #69 : périmètre exact de la validation live utilisateur sur téléphone
  réel (lots 1 et 2 validés en réel via l'appairage puis le workspace ; lot 3 —
  `TerminalKeyBar` et invariant de focus — NON exercé), arbitrage Main
  autorisant le merge avec réserve écrite, et report de l'écart Playwright
  vers #63 plutôt que vers un ticket neuf.
- Faits de topologie mis à jour après rebase sur `develop@fe0e53e` : les SHA
  cités (`e22ea5b`/`8f15ad6`/`6ed0087`) n'existaient plus sur aucune branche.
- Journal des tâches de fond : complétions enregistrées.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 15:15:45 +02:00
f0f2f3b497 fix(frontend): retirer l'import vi inutilisé du test de la key bar (#69)
`npm run build` est `tsc --noEmit && vite build` : un import non utilisé
casse le build (TS6133), pas seulement le lint.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 15:13:22 +02:00
62ecefe1e8 feat(frontend): terminal pilotable au doigt sur téléphone (#69)
Lots 3 et 4 du ticket #69. Sans ça un agent CLI est indriveable depuis un
téléphone : le clavier virtuel n'a ni Esc, ni Tab, ni Ctrl, ni flèches, donc
on peut taper un prompt mais pas l'interrompre, compléter un chemin, sortir
d'un éditeur ou rappeler l'historique.

- `TerminalView` expose un `onReady(api)` optionnel (donc desktop inchangé,
  et inerte quand xterm ne monte pas). `api.send` passe par `term.input()` :
  le *même* chemin qu'une frappe réelle, donc le relais PTY, le comptage de
  lignes et la suspension du write-portal s'appliquent à l'identique. Écrire
  sur le handle aurait court-circuité le portal.
- `TerminalKeyBar` (web-only) : Esc/Tab/Ctrl-C/Ctrl-D/flèches en chips
  tactiles 44px, scroll horizontal. Chaque tap annule le déplacement de
  focus et refocalise xterm — sinon le clavier virtuel se referme à chaque
  touche.
- La cellule agent passe de `h-64` fixe (une letterbox d'~20 lignes) à
  `h-[60dvh]` sur téléphone, `sm:h-80` au-delà.
- Tests : séquences d'octets émises, préservation du focus, état désactivé
  avant montage de xterm, et garde de non-régression sur la frontière —
  le client web ne monte aucun layout-grid/split/dock desktop.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 15:13:22 +02:00
48b8853214 feat(frontend): shell web responsive pour viewport téléphone (#69)
Lots 1 et 2 du ticket #69 : rendre le client web (#13) utilisable sur un
téléphone. Le client web n'a jamais eu de docks ni de fenêtres flottantes —
il est déjà une colonne verticale unique — donc le lot 2 se réduit aux
rangées qui débordaient réellement à 360px.

- `100dvh` sur html/body/#root : `height: 100%` se résout sur le *large*
  viewport, donc la barre d'URL rétractable d'un navigateur mobile masquait
  le bas de l'app. Desktop inchangé (dynamic == large viewport).
- `viewport-fit=cover` + padding `env(safe-area-inset-*)` sur le header et
  le workspace : l'app peut peindre sous une encoche sans perdre ses
  contrôles. Le zoom reste libre (accessibilité).
- Padding responsive (`p-4 sm:p-6`) : 48px de gouttière sur 360px, c'était
  13% de la largeur.
- `AgentLiveRow` et la rangée de tâche de fond empilent leurs contrôles sur
  téléphone (nom / badges / actions) et retrouvent leur ligne unique dès
  `sm` — à 360px les boutons Cancel+Retry ne tenaient pas.
- Code d'appairage : `inputMode="numeric"`, pas d'autocapitalisation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 15:13:22 +02:00
fe0e53ec59 Merge feature/ticket65-idea-serve-headless into develop (#65)
Intègre le serveur headless `idea-serve` : le cœur (use cases, DTO, events)
devient partagé entre deux driving adapters, le desktop Tauri et le crate
`web-server` autonome, sans dépendance GUI.

Vert avant merge : `cargo test -p backend` 41 · `-p web-server` 55 ·
`-p app-tauri` 35, 0 échec, ré-exécuté sur la branche. Invariant headless
vérifié (`ldd idea-serve` sans webkit/gtk/tauri) et validation live utilisateur
du serveur (démarrage, SPA servi, contrôle d'origine strict, accès distant).

Débloque #68 (activer le serveur depuis le desktop, via `run_embedded`) et #66
(Docker).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 15:02:28 +02:00
5ee25d1ef6 chore(ideai): état d'orchestration du sprint serveur headless (#65, #68-#70)
État durable de `.ideai/` accumulé pendant le chantier `idea-serve`, séparé du
code applicatif conformément aux précédents `8e481ae` / `ad1f225`.

- Tickets : #13 clôturé (server/client mode livré), #65 passé en QA avec son
  carnet de chantier complet (arbitrages Architect sur le propriétaire canonique
  des DTO, structure livrée, vérif QA, incident de topologie et sa leçon).
  #64/#66/#67 rattachés au sprint. Nouveaux tickets #68 (activer le serveur
  depuis le desktop, dépend de #65), #69 (adaptabilité client téléphone, en QA)
  et #70 (gestion des modèles locaux llama.cpp).
- Mémoire : note `web-client-is-single-column-no-desktop-shell` — le client web
  n'a pas de shell desktop, à lire avant tout cadrage responsive sur cette
  surface.
- Journal des tâches de fond : complétions enregistrées.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 15:02:14 +02:00
4f57e5a0db chore(gitignore): ignorer la sortie du build web frontend/dist-web (#65)
Le build web (`VITE_TRANSPORT=http`) que sert `idea-serve` produit
`frontend/dist-web/`, remonté comme non suivi car la règle existante n'ancrait
que `frontend/dist/`. C'est de la sortie de build (~1 Mo, rebuildable depuis les
sources) : même classe que `dist/`, elle n'a pas sa place dans l'historique.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 15:01:40 +02:00
0e15482ce2 feat(web-server): extraire le serveur headless idea-serve du binaire Tauri (#65)
Sort le serveur HTTP/WS de `app-tauri` vers un crate `web-server` autonome,
exposant le binaire `idea-serve` qui sert le client web sans aucune dépendance
GUI. Le cœur (use cases, DTO, events) devient partagé entre deux driving
adapters : le desktop Tauri et le serveur headless.

Structure :
- `crates/web-server` : lib + bin `idea-serve`, consomme `backend::{dto, events}`.
- `crates/backend` : `dto.rs` et `events.rs` deviennent le propriétaire canonique
  des DTO/events transport-neutres (arbitrage Architect : pas de crate contrat
  dédié, `backend` est déjà le composition root transport-neutre).
- `crates/app-tauri` : `dto.rs` réduit à un shim de ré-export, `events.rs` réduit
  au relais Tauri (souscription au bus + emit), `server.rs` vidé au profit du
  crate partagé.
- `Cargo.toml`/`Cargo.lock` : `web-server` ajouté aux membres du workspace.

Frontière respectée : les DTO de `backend` ne tirent ni Tauri, ni HTTP/WS, ni
Axum/Hyper, ni UI ; `domain`/`application` n'en dépendent pas. Le frontend est
inchangé (`git diff 506d589...HEAD -- frontend` vide).

`web_server::run_embedded(...)` est le point d'extension prévu pour #68 (le
desktop consommera `web-server` comme lib plutôt que d'en forker le serveur).

QA (exécution réelle, re-vérifiée avant merge) :
- `cargo test -p backend` 41 passed · `-p web-server` 55 passed · `-p app-tauri`
  35 passed, 0 échec.
- Invariant headless : `ldd target/debug/idea-serve` ne tire aucun
  webkit/javascriptcore/gtk/gdk/soup/tauri/wry, là où `app-tauri` les tire bien.
- Validation live utilisateur : `idea-serve` démarre, sert le SPA, le contrôle
  d'origine strict tient, accès distant OK via reverse proxy.
- Les 10 échecs `openai_compat` de `cargo test --workspace` sont préexistants sur
  `506d589` (bind réseau interdit par la sandbox), hors périmètre.

Squash de `82e8e77` (commit de sûreté « état intermédiaire non figé », qui
rapatriait le travail réalisé par erreur sur la branche de #69) et de `ddbea7b`
(déduplication des DTO events, comblant l'écart annoncé par le premier). Les deux
n'avaient de sens qu'ensemble : ce commit fige le contrat que le premier laissait
explicitement ouvert. Arbre identique bit pour bit à `ddbea7b` ; historique
d'origine conservé sur `backup/ticket65-pre-squash-ddbea7b`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 15:01:18 +02:00
506d58956b Merge feature/server-client-packaging into develop (#13)
Transport HTTP/WS et surface web du ticket #13, validé par l'utilisateur en
lancement Desktop (comportement conforme).

Contenu : endpoint PTY WebSocket authentifié et relais live-state/background
côté serveur (idea --serve), client web read-only, xterm.js sur WebSocket avec
reconnexion et scrollback, surfaces agent et live web, service same-origin de
dist et durcissement (logout, logs de sécurité, static hardening).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 10:32:46 +02:00
8e481aed69 chore(ideai): état d'orchestration du sprint #13 (transport HTTP/WS + surface web)
Versionne l'état runtime IdeA produit pendant le sprint #13 : store de
tickets (dont les nouveaux #55 à #67), compteur et index, notes de mémoire
projet des lots F0 à F5, et journal des tâches de fond.

Séparé du code applicatif conformément à la convention du dépôt
(cf. ad1f225, a244f32) : métadonnée d'orchestration last-writer-wins,
sans impact sur le build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 10:32:39 +02:00
c246875f6d fix(frontend): connecter le WS live sur /api/ws conforme au contrat (#13)
Le client live visait /ws/live alors que le serveur expose /api/ws : la
connexion échouait et la reconnexion bouclait en permanence. Chemin corrigé
et extrait en constante WS_PATH, avec deux tests de non-régression sur
l'URL construite.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 09:14:37 +02:00
de9c90966d fix(frontend): binder le fetch global à globalThis (pairing Firefox) (#13)
Firefox exige que fetch soit appelé sur Window : passer la référence nue
`fetch` en dépendance déclenchait « 'fetch' called on an object that does
not implement interface Window » au pairing. Nouveau helper defaultFetch()
qui binde globalThis.fetch, utilisé par webSession.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 08:47:00 +02:00
c9ce3d7c4e fix(server): aligner app-data-dir sur l'identifier desktop + corriger doc build web (#13)
default_app_data_dir aligné sur l'identifier tauri app.idea.ide, avec
précédence IDEA_APP_DATA_DIR > XDG_DATA_HOME > HOME. Log stderr
`idea --serve: app data dir = <path>` au démarrage et test de précédence.
Doc build web corrigée (VITE_TRANSPORT=http npx vite build + npm au lieu
de pnpm), section app-data-dir et avertissement double-writer desktop/serve.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 08:39:22 +02:00
6117177957 feat(frontend): polish web (logout, 401→pairing, reconnexion globale) (#13)
Flow logout : POST /api/logout puis retour à l'écran de pairing et
déconnexion du live. 401 renvoie au pairing sans boucle. Bannière de
reconnexion couvrant le cas live-only et le serveur indisponible.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 08:23:11 +02:00
d538808a0d feat(server): servir dist same-origin + durcissement (logout, logs sécurité, static hardening) (#13)
Sert le bundle web (dist/) en same-origin via --web-root / IDEA_WEB_ROOT
ou le web/ packagé. serve_static durci : anti path-traversal, typage MIME,
X-Content-Type-Options nosniff, CSP, fallback SPA. Ajoute POST /api/logout
qui révoque la session HTTP + WS, et une journalisation sécurité via
SecurityLogger. Documente le déploiement remote (dev local, packaging web/,
reverse proxy HTTPS).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 08:10:31 +02:00
dd1d083a1a feat(frontend): surfaces live web (workstate live, background, inbox, reconnect) (#13)
Lot F5 du chantier server/client mode : le client web consomme les frames
event.domain (B7) pour animer ses surfaces live.

- webLive.ts : consommation du flux event.domain (workstate, background,
  inbox).
- useLiveReconnect.ts : reconnexion du flux live.
- wsLiveClient.ts / index.ts : câblage du transport live.
- WebWorkspace.tsx : surfaces live branchées.
- Tests : WebWorkspaceLive.test.tsx, wsLiveClientReconnect.test.ts,
  WebApp.test.tsx.

Validé : frontend 753 tests verts, desktop non régressé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 23:37:02 +02:00
1bc5217dbc feat(server): relais live-state + actions background via web (event.domain, allowlist) (#13)
Lot B7 du chantier server/client mode : le serveur --serve relaie l'état
live et les tâches de fond au client web.

- Relais du bus domaine en frames event.domain, queue bornée avec drop,
  exclusion de PtyOutput.
- Allowlist étendue : list_background_tasks (read) + cancel_background_task
  / retry_background_task (actions), sous auth.
- 49 tests server (fan-out, drop sur queue pleine, relais de complétion
  background, exclusion PtyOutput, actions allowlist + auth). Clippy propre.

Validé : app-tauri 292+ tests verts, backend 28, cœur backend agnostique,
desktop non régressé. Réserve connue : le fan-out socket réel relève d'une
validation live hors sandbox.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 23:36:50 +02:00
5254f16095 feat(frontend): surface agent web sur WebSocket (cellule agent) (#13)
Lot F4 du chantier server/client mode : le client web expose une cellule
agent branchée sur le PTY WebSocket, en face de agent.launch (B6).

- wsLiveClient.ts : launchAgent sur le transport WebSocket.
- streamGateways.ts : gateway agent alignée sur le contrat serveur B6.
- WebAgentCell.tsx : cellule agent web, câblée dans WebWorkspace et index.
- Tests : agentGateway.test.ts, WebApp.test.tsx.

Validé : frontend 749 tests verts, contrat B6↔F4 aligné (aucun écart de
frame), desktop non régressé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 23:17:24 +02:00
6391d1c75a feat(server): agents CLI via le PTY WebSocket sur idea --serve (#13)
Lot B6 du chantier server/client mode : le serveur --serve permet de lancer
et piloter un agent CLI à travers le PTY WebSocket B5.

- Frame WS agent.launch → ack terminal.attached avec assignedConversationId.
- Réutilise LaunchAgent + TerminalSessions + sink WS B5, réattache sans
  respawn, singleton AGENT_ALREADY_RUNNING, structured → UNSUPPORTED.
- 42 tests server (launch/reattach/singleton/structured), tests flaky
  stabilisés. Clippy server.rs propre.

Validé : app-tauri 286 tests verts, backend 28, contrat B6↔F4 aligné
(aucun écart de frame), desktop non régressé. Réserve connue : le
round-trip socket réel relève d'une validation live hors sandbox.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 23:17:11 +02:00
7487902ddb feat(frontend): xterm.js sur WebSocket avec reconnexion et scrollback (#13)
Lot F3 du chantier server/client mode : le client web branche xterm.js sur
le terminal distant via WebSocket, en face de l'endpoint PTY B5.

- wsLiveClient.ts : transport WebSocket du terminal avec reconnexion.
- streamGateways.ts : gateway terminal (open/attach/data/resize/close).
- frames.ts : frames PTY alignées sur le contrat serveur B5.
- Tests : terminalGateway.test.ts, wsLiveClientReconnect.test.ts.

Validé : frontend 743 tests verts (F3 ciblé 12), cohérence des frames
B5↔F3 confirmée, desktop non régressé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 18:09:25 +02:00
917be995a4 feat(server): endpoint PTY WebSocket authentifié sur idea --serve (#13)
Lot B5 du chantier server/client mode : le serveur --serve expose le
terminal distant via un endpoint WebSocket, préalable au client xterm (F3).

- Handshake WebSocket RFC 6455 fait main, sans nouvelle dépendance.
- Auth de l'upgrade par cookie de session + Origin strict.
- Frames PTY (open/attach/close/ping), réattache, scrollback, backpressure.
- 37 tests server dont les refus de sécurité et les handlers
  open/attach/close/ping. Clippy propre (result_large_err corrigé).

Validé : app-tauri 277 tests verts, backend 28, cohérence des frames
B5↔F3 confirmée, desktop non régressé. Réserve connue : le round-trip
socket réel relève d'une validation live hors sandbox.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 18:09:14 +02:00
e457152c66 Merge feature/ticket13-server-client-mode into develop (#13)
Premier incrément livrable du chantier server/client mode (B0→B4/F2) :
cœur backend commun extrait hors Tauri, sink de stream agnostique, serveur
HTTP sécurisé idea --serve (pairing + cookie de session, allowlist
read-only), et client web read-only (pairing, liste, ouverture, snapshot).

Desktop inchangé ; mode web opt-in via VITE_TRANSPORT="http". PTY WebSocket
(B5) + xterm (F3) reportés à la 2e vague. Incrément VERT de bout en bout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 13:25:59 +02:00
e500e31663 feat(frontend): client web read-only pairing + snapshot état (#13)
Lot F2 du chantier server/client mode : client web read-only complétant le
premier incrément livrable — pairing, liste des projets, ouverture et
snapshot de l'état, sans PTY.

- frontend/src/adapters/http/webSession.ts : session web (pairing/cookie).
- frontend/src/features/web : PairingScreen, WebWorkspace, WebApp, index.
- Câblage main.tsx et adaptations httpInvoker.ts / index.ts (cas 401).
- Tests : webSession.test.ts, WebApp.test.tsx, cas 401 dans
  httpInvoker.test.ts.

Validé : frontend 736 tests verts, build vert, garde no-direct-invoke
verte, contrat B4↔F2 aligné, desktop non régressé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 13:21:35 +02:00
fa353f6c0b feat(server): allowlist read-only (get_project_work_state, open_project read-only) (#13)
Lot B4 du chantier server/client mode : extension read-only de l'allowlist
du serveur --serve pour clôturer le premier incrément livrable (sans PTY).

- open_project exposé en read-only via resolve_project_readonly.
- get_project_work_state ajouté à l'allowlist.
- 4 nouveaux tests.

Validé : cargo check --workspace vert, app-tauri 265 tests verts (dont les
4 tests B4), contrat B4↔F2 aligné (list_projects/open_project/
get_project_work_state), desktop non régressé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 13:21:28 +02:00
4ed0b168d2 feat(server): sous-commande idea --serve avec serveur HTTP sécurisé pairing+cookie (#13)
Lot B3 du chantier server/client mode : socle serveur sécurisé exposant le
cœur backend en mode client/serveur, sans PTY (reporté B5).

- crates/app-tauri/src/server.rs : sous-commande --serve, serveur HTTP
  sécurisé — /api/invoke sur allowlist, /api/pair → cookie de session,
  Origin strict, refus du secret en query, gate de bind. 16 tests dont les
  2 refus de sécurité (pairing_rejects_wrong_code,
  invoke_rejects_invalid_session_cookie).
- crates/app-tauri/src/lib.rs : câblage de la sous-commande --serve.
- crates/app-tauri/src/commands.rs : adaptations.
- crates/app-tauri/Cargo.toml : deps bytes/cookie/http/http-body-util,
  feature tokio net. Cargo.lock synchronisé.

Validé : cargo check --workspace vert, app-tauri 267 tests verts, cœur
backend agnostique confirmé, desktop non régressé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 13:06:40 +02:00
e0cdb4aa56 feat(frontend): adapter web HTTP+WebSocket derrière les ports (#13)
Lot F1 du chantier server/client mode : nouvel adaptateur web branché
derrière les ports d'invocation et de flux live, permettant au frontend de
dialoguer avec le backend via HTTP + WebSocket en mode client/serveur. Le
mode desktop (Tauri IPC) reste inchangé.

- frontend/src/adapters/http : invoker HTTP, client live WebSocket, gateways
  request/response et stream, frames, garde unsupported (7 fichiers + 2 tests).
- frontend/src/app : câblage DI (di.tsx) et son test, typage vite-env.d.ts.

Validé : build vert, garde no-direct-invoke verte, 724 tests verts,
desktop inchangé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 12:50:38 +02:00
c8fef2a76a refactor(backend): abstraire le sink de stream hors de tauri::ipc::Channel (#13)
Lot B2 du chantier server/client mode : le cœur backend émet désormais ses
flux via une abstraction de sink agnostique, sans dépendre directement de
tauri::ipc::Channel, préalable au futur serveur web + PTY WebSocket.

- crates/backend : abstraction de sink (stream.rs) câblée dans lib.rs.
- crates/app-tauri : implémentation Tauri du sink (stream.rs) et adaptation
  des surfaces lib.rs, pty.rs, chat.rs.
- Nettoyage clippy des 2 warnings B2.

Validé : cargo check --workspace vert, tests backend/app-tauri verts,
cœur agnostique Tauri, clippy B2 propre.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 12:50:29 +02:00
955db79e97 refactor(backend): extraire le cœur backend commun hors Tauri (#13)
Lot B1 du chantier server/client mode : création de la crate `backend`
qui héberge le cœur commun (endpoint MCP, outils OpenAI) indépendant de
Tauri, préalable au futur serveur web + PTY WebSocket.

- crates/backend : nouvelle crate (lib.rs, mcp_endpoint.rs, openai_tools.rs).
- crates/app-tauri : câblage sur la crate backend (state.rs, mcp_bridge.rs,
  Cargo.toml, tests/orchestrator_wiring.rs).
- Cargo.toml / Cargo.lock racine : ajout de la crate au workspace.

Validé : cargo check --workspace vert, tests backend/app-tauri verts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 09:50:17 +02:00
5505acc1f6 docs(ticket13): inventaire transport backend et frontend (B0)
Lot B0 du chantier server/client mode : cartographie des surfaces de
transport existantes, préalable à l'extraction du cœur backend commun.

- docs/ticket13-b0-backend-transport-inventory.md : inventaire backend.
- docs/ticket13-f0-frontend-transport-inventory.md : inventaire frontend.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 09:50:10 +02:00
ebad5ff9f2 Merge feature/ticket60-ticket-assistant-no-reply into develop (#60)
Rendre visible l'échec « aucune réponse » de l'assistant de ticket :
stream sans Final ou Final vide → ReplyChunk::Error terminal visible,
plus jamais de tour muet. Backend + frontend, tests verts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 08:48:04 +02:00
18e6bedb8b Merge feature/ticket55-model-warmup-deadline into develop (#55)
Deadline de warmup configurable pour le cold-start llama.cpp.
Tests verts (domain/application/infrastructure + dto app-tauri).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 08:47:59 +02:00
f7cae4c0a8 fix(model-server): deadline de warmup configurable pour le cold-start llama.cpp (#55)
Corrige la régression high « Error on loading local model » : le premier chargement
du serveur modèle local (cold-start llama.cpp) dépassait la fenêtre de readiness et
échouait. Le warmup dispose désormais d'un deadline par défaut de 600 s, surchargable
par config optionnelle `warmup_deadline_secs` (validée dans [30, 1800]).

- domain: champ `warmup_deadline_secs: Option<u64>` + validation de borne
- application: policy de readiness effective (défaut 600 s, override par config)
- app-tauri: DTO `warmupDeadlineSecs`
- infrastructure: application de la deadline effective au warmup

Verdict QA (vert) : domain 252, application 81 + 22 model_server, app-tauri
dto_model_servers 6, infrastructure model_server 2, build OK. Contrat readiness
couvert sur ports mockés.
Caveat : le cold-start end-to-end réel (llama-server) sort du sandbox de test ->
vérification manuelle utilisateur restante, non couverte par les tests unitaires.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 08:41:44 +02:00
2f7f111545 fix(ticket-assistant): rendre visible l'échec « aucune réponse » de l'assistant de ticket (#60)
Introduit un variant terminal `ReplyEvent::Error { message }` (domain/ports)
propagé jusqu'au chat : l'adapter OpenAI-compat récupère `reasoning_content`
quand `content` est vide, et un `Final` vide est converti en `Error` visible
plutôt qu'un tour silencieux qui pend. Le front (`ReplyChunk` + useTicketAssistant)
rend ce message d'erreur dans la cellule.

- domain: variant `ReplyEvent::Error`, bras non terminal (readiness, structured drain)
- infra/session: parse `reasoning_content` (delta/completion), fallback visible
- app-tauri: mapping ReplyEvent::Error -> ReplyChunk::Error, Final vide -> Error
- frontend: ReplyChunk `error`, rendu dans useTicketAssistant (+ test .test.tsx)

NB: commit sur la feature branch, PAS un merge. #60 reste inProgress.
Les 10 tests `cargo test -p infrastructure --lib session` échouent SOUS SANDBOX
uniquement (bind loopback 127.0.0.1:0 interdit -> Os PermissionDenied), pas une
régression : revérification hors-sandbox requise avant tout merge vers develop.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 08:32:59 +02:00
ce5aa2811c Merge feature/ticket2-ptyport-wait-decouple-output into develop (#2)
PtyPort::wait/try_wait ajoutés au port figé (exit status mémorisé, idempotent) ;
le runner de tâches de fond détecte la fin via wait au lieu de l'EOF du drain
output, découplant cycle de vie du process et flux de sortie. Fakes PtyPort du
workspace complétés. Tee live UI hors périmètre (#58). QA vert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 23:42:48 +02:00
62b71776f5 fix(background-task): découpler la détection de fin de tâche du drain output PTY (#2)
Le runner de tâches de fond détectait la fin d'un process via l'EOF du drain de
sortie PTY, couplant le cycle de vie du process au flux d'output. Un output
encore ouvert (ou drainé ailleurs) pouvait masquer ou retarder la détection de
fin.

Ajoute wait/try_wait au port figé PtyPort :
- wait : bloquant, rend un ExitStatus idempotent ;
- try_wait : non bloquant, rend Option<ExitStatus> ;
- exit status mémorisé pour garder wait/try_wait/kill cohérents.
Implémentation dans PortablePtyAdapter (état d'exit mémorisé). Le
CommandBackgroundRunner détecte désormais la fin via pty.wait (select sur
cancel/deadline) au lieu de l'EOF du drain. Tous les fakes PtyPort du workspace
sont complétés en conséquence.

Le tee live UI reste hors périmètre (traité en #58). Aucun breaking IPC/front.

Tests : infrastructure/tests/background_task_runner.rs (nouveau) + pty_adapter.rs
verts, non-régression application/app-tauri OK (hors échecs réseau du sandbox).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 23:42:40 +02:00
c6e34483d3 Merge feature/ticket31-detectprofiles-parallel-probes into develop (#31)
Sondes de DetectProfiles::execute parallélisées (une tâche Tokio par candidat,
JoinHandle attendus dans l'ordre) : coût total ~1×timeout au lieu de N×800ms,
ordre de sortie déterministe préservé. QA vert 20/20.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 19:32:04 +02:00
2d7396a86b perf(agent): paralléliser les sondes de DetectProfiles::execute (#31)
Les sondes de détection de profils étaient exécutées séquentiellement, portant
le coût total au pire à N×800 ms (N × timeout par candidat).

Chaque candidat est désormais sondé dans une tâche Tokio dédiée (tokio::spawn),
les JoinHandle étant attendus dans l'ordre de création : le coût total tombe à
~1×timeout tout en préservant un ordre de sortie déterministe. Aucune dépendance
ajoutée, aucun changement de contrat.

Test : crates/application/tests/profile_usecases.rs (concurrence multi-thread +
ordre déterministe). profile_usecases 20/20, suite application verte.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 19:31:57 +02:00
51b5d07f35 Merge feature/ticket34-chatbridge-scrollback-bounded into develop (#34)
Scrollback de ChatBridge borné (double cap chunks + octets, drop par la tête)
pour stopper la croissance mémoire du buffer de replay. Aucun changement DTO.
QA vert 19/19.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 19:26:37 +02:00
b4b86cdc0e fix(chat): borner le scrollback de ChatBridge (#34)
Le scrollback de ChatBridge croissait sans limite, faisant enfler la mémoire sur
les conversations longues. Il sert de buffer de replay transport (reattach), pas
d'historique durable.

Introduit un double cap : MAX_CHAT_SCROLLBACK_CHUNKS=2000 et
MAX_CHAT_SCROLLBACK_BYTES=512 KiB. Un helper trim_scrollback, appelé après chaque
send_output, drop des chunks entiers par la tête tout en conservant l'ordre et
les chunks les plus récents. Doc de chat.rs corrigée pour refléter cette nature
de buffer borné.

Aucun changement de ReplyChunk / reattach_agent_chat / DTO. Tests : suite
chat_bridge 19/19.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 19:26:32 +02:00
63a7b6165f Merge feature/ticket20-ticketlist-ref-match-opaque-cursor into develop (#20)
ticket_list : matching exact du numéro/#ref dans la recherche texte + curseur
de pagination opaque et stable (anchor-based v1, rejet explicite des curseurs
invalides). DTO cursor inchangé (non-breaking UI). Tests #20 verts 7/7.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 19:20:49 +02:00
bd5d8d864a fix(tickets): matching exact du #ref et curseur de pagination opaque/stable pour ticket_list (#20)
Épurement de la dette de ticket_list sur deux axes :

Recherche texte — matching exact du numéro/#ref via parse_issue_search_ref,
court-circuité avant load_issue, sans matching par substring numérique
(« 1 » ne remonte plus « 12 », « 123 »…).

Pagination — curseur opaque et stable anchor-based au lieu d'un offset fragile :
token v1.<base64url-no-pad-json> encodant le tri + l'ancre {number, sortKey},
reprise strictement après l'ancre. Curseur legacy / invalide / de version
inconnue / avec sort divergent rejeté par une erreur explicite « Invalid
cursor ». Le DTO cursor reste String → non-breaking côté UI.

Fichiers : infrastructure/src/issues.rs, app-tauri/src/tickets.rs,
app-tauri/Cargo.toml (+ base64 0.22). Tests : issue_store_text_filter,
ticket_list_ 7/7 (anchor-based + rejets).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 19:20:40 +02:00
83e4df7626 Merge feature/ticket33-structured-routing-explicit into develop (#33)
Routage structuré vs PTY rendu explicite dans LaunchAgent
(StructuredRoutingMode) : erreur explicite au lieu du fallthrough silencieux
quand un profil structuré rencontre une factory non câblée. QA vert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 19:08:32 +02:00
67101ba607 fix(agent): rendre explicite le routage structuré vs PTY dans LaunchAgent (#33)
Un profil structuré rencontrant une factory de session non câblée retombait
silencieusement sur pty.spawn (fallthrough du `if let`), masquant une erreur de
configuration au lieu de la signaler.

Remplace ce fallthrough par une intention explicite :
- nouvel enum StructuredRoutingMode { HumanPtyFallback, RequireStructured } sur
  LaunchAgent, posé via le builder with_structured_routing_mode ;
- le `if let` devient un `match` explicite (agent/lifecycle.rs) ; en mode
  RequireStructured, un profil structuré sans factory câblée retourne
  AppError::Process("structured profile requires structured session factory")
  au lieu de tomber sur pty.spawn.

Composition root (app-tauri/src/state.rs) : launcher humain = HumanPtyFallback,
launcher orchestrateur = RequireStructured, wake background rebranché sur
orchestrator_launch_agent.

Tests : agent_lifecycle.rs (4 branches de routage) + non-régression
agent_wake/structured_launch_d3. Crate application verte.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 19:08:23 +02:00
e24bb5f1f4 Merge feature/ticket56-agent-selector-visible-elsewhere into develop (#56)
Le sélecteur d'agent de LayoutGrid dérive « visible ailleurs » du layout
courant : un agent live non réellement affiché redevient sélectionnable, sans
tuer la session. QA vert 706/706.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 18:56:40 +02:00
4099b0d1c4 fix(layout): rendre resélectionnable un agent live non réellement affiché (#56)
Le sélecteur d'agent de LayoutGrid désactivait un agent en se fondant sur le
seul liveAgents.nodeId : un agent live dont l'ancienne cellule affiche désormais
un autre agent restait grisé (« visible ailleurs ») alors qu'il n'était plus
affiché nulle part, donc impossible à resélectionner.

La dérivation « visible ailleurs » s'appuie maintenant sur le layout courant
(feuille visible ≠ cellule courante ET leaf.agent === candidate). Un agent live
dont l'ancienne cellule montre un autre agent redevient sélectionnable dans une
autre cellule ; la session n'est jamais tuée (attachLiveAgent conservé). Le prop
mort visibleNodeIds est retiré du threading.

Tests : frontend/src/features/layout/singletonAgent.test.tsx. Suite frontend
verte 706/706.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 18:56:34 +02:00
fd5c8932ca Merge feature/ticket55-model-server-readiness into develop (#55)
Fiabilisation de la readiness au démarrage d'un serveur modèle local
(llama.cpp) : deadline de warmup longue, distinction vivant-en-warmup /
process mort. QA vert 20/20.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 18:43:54 +02:00
141c13dabb fix(model-server): fiabiliser la readiness au démarrage d'un serveur modèle local (#55)
Le serveur modèle local (llama.cpp) était tué après ~5 s par un timeout de
readiness prématuré, alors que le modèle était encore en warmup (chargement en
RAM/VRAM). Résultat : « Error on loading local model » alors que le process
était vivant et en train de démarrer normalement.

Introduit une deadline de warmup longue configurable (~120 s via
ReadinessPolicy) qui distingue un process « vivant en warmup » d'un process
« mort » :
- Ready                → succès immédiat
- Unreachable + Running → continuer d'attendre (warmup en cours)
- Unreachable + Exited  → échec rapide (process mort, inutile d'attendre)
- deadline atteinte     → stop + Timeout

Tests (crates/application/tests/model_server.rs) : warmup lent, exit pendant le
warmup, deadline atteinte, plus la régression adaptée. 20/20 verts, crate
application verte.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 18:43:46 +02:00
e0ee24e3b5 Merge feature/ticket54-model-download-progress into develop (#54)
Stretch B2/F2 : progression fine du téléchargement du modèle llamacpp,
par-dessus le MVP (B1/F1) déjà intégré. Tests verts (application +
infrastructure + vitest, tsc clean).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 10:37:33 +02:00
ad1f2257e6 chore(ideai): état d'orchestration du sprint #54 (progression téléchargement)
Métadonnée runtime IdeA : tickets, sprints, mémoire et background-tasks
mis à jour au fil du sprint de progression fine du téléchargement (#54).
Séparé du code de feature (last-writer-wins, état non applicatif).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 10:37:23 +02:00
2183dfd291 feat(model-server): progression fine du téléchargement du modèle llamacpp (#54)
Stretch B2/F2 de #54, par-dessus le MVP déjà mergé (B1/F1).

Backend : le port de téléchargement HF publie une progression débouncée
(bytes reçus / total, pourcentage) via le stream de statut du serveur
modèle, avec gestion du total inconnu (pas de faux %), du cache hit,
de l'annulation et du timeout.

Frontend : l'overlay plein-cellule de préparation du serveur affiche la
progression réelle (barre, %, octets, source) en mappant le fil de
statut, avec la règle « pas de faux % » quand le total est inconnu.

Tests : application + infrastructure (téléchargement débouncé, cancel,
timeout, cache hit, total inconnu) et vitest (overlay + formatage pur).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 10:37:14 +02:00
bd335c3a1c Merge feature/ticket54-llamacpp-model-download into develop (#54)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 23:14:13 +02:00
fe7ed0aa20 feat(model-server): afficher le téléchargement du modèle llamacpp au démarrage (#54)
Ajoute un handle du téléchargement des modèles lors du démarrage de
llamacpp : le domaine et l'application émettent la progression de
téléchargement du modèle, relayée en événement côté app-tauri, et l'UI
l'affiche via un badge de lancement et un overlay de cellule pendant que
le serveur de modèle démarre.

Backend (B1) : progression de téléchargement dans domain/application,
relais d'événement app-tauri, couverture de tests.
Frontend (F1) : modelServerLaunch, badge et overlay LayoutGrid, tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 23:14:05 +02:00
c67ec4f7bd Merge feature/ticket44-firstrun-keep-profile-info into develop (#44)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 22:53:58 +02:00
8cdc44de6e feat(first-run): conserver les infos du profil courant à l'édition (#44)
Extrait la logique de dérivation des entrées du wizard dans un module
dédié `wizardEntries` afin de préserver les informations du profil
courant lors de l'ouverture de l'affichage d'édition des profils, au
lieu de repartir d'un état vide. Couvre le comportement par des tests
unitaires (wizardEntries + FirstRunWizard).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 22:53:46 +02:00
76560ab101 Merge feature/ticket50-open-windows-menu-state into develop
Ticket #50 : commande backend `list_open_view_windows` + consommateur
frontend qui réconcilie l'état du menu Panneaux avec les fenêtres
détachées réellement ouvertes. Validé QA vert (frontend
typecheck+build+677 tests ; backend cargo check/build + 7 tests
view_window).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 18:15:09 +02:00
b4a01e0ae9 fix(view-windows): réconcilier l'état des fenêtres détachées au boot (#50)
Branche le consommateur frontend sur la commande backend
`list_open_view_windows` : le port window expose la liste des fenêtres
de panneaux détachées réellement ouvertes côté OS, l'adaptateur Tauri
(et son double mock) l'implémente, et `ProjectsView` réconcilie le
placement des vues au démarrage à partir de ce snapshot au lieu de
supposer un état. Couvre `viewPlacement` et la réconciliation par des
tests dédiés.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 18:14:45 +02:00
c1051ce2f5 Merge feature/ticket52-add-project-button-tabs into develop
Ticket #52 : le bouton « + » d'ajout de projet est collé aux onglets
(retrait des `flex-1`). Validé QA vert (typecheck + build + 665 tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 18:04:24 +02:00
f7f56e8583 fix(projects): coller le bouton "+" aux onglets de projet (#52)
Retire les utilitaires `flex-1` sur la liste d'onglets et sur le
placeholder « No open tabs. » : le conteneur ne pousse plus la zone
d'onglets sur toute la largeur, si bien que le bouton d'ajout de projet
« + » vient directement à la suite des onglets au lieu d'être repoussé à
l'extrémité droite de la barre.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 18:04:16 +02:00
ea7a98be03 Merge chore/fmt-agent-lifecycle into develop
Applique rustfmt sur agent_lifecycle.rs pour que develop repasse
`cargo fmt --check`. Fix de formatage pur, aucun changement de comportement.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 18:01:14 +02:00
7ddf4d46b9 chore(fmt): appliquer rustfmt sur agent_lifecycle.rs
Reformate `launch_opencode_omits_api_key_when_profile_has_none` selon
rustfmt (le fichier committé échouait `cargo fmt --check`). Aucun
changement de comportement, purement du formatage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 17:59:26 +02:00
afbf315e97 feat(view-windows): exposer list_open_view_windows pour l'état du menu
Ajoute la commande Tauri `list_open_view_windows` qui retourne un
`ViewWindowSnapshot` (panel, label, visible) par fenêtre de panneau
détachée, avec le parseur `view_panel_from_window_label` (labels stables
et legacy suffixés du project id). Enregistre la commande dans le
handler. Backend seul pour le ticket #50 ; pas encore de consommateur
frontend.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 17:59:15 +02:00
9326a4897a chore(memory): nettoyer les notes transitoires et supersédées (#49)
Suppression de 31 notes de mémoire projet devenues transitoires
(checkpoints, plans de test datés, findings résolus) ou supersédées, et
remise en parité de l'index MEMORY.md (52 entrées = 52 fichiers).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 13:20:07 +02:00
8bbd8d3d68 Merge feature/ticket30-session-limit-handle-main into develop
Fix du handle de limite de session sur le chemin direct (#30) + émission
AgentRateLimited sur le chemin délégué (#7/F2). QA verte.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 13:19:46 +02:00
225890b57a fix(session-limit): émettre AgentRateLimited sur le chemin délégué (#7)
Sur le rendez-vous délégué (idea_ask_agent), quand la cible touchait sa
limite de session, l'événement AgentRateLimited n'était pas émis : l'écart
backend documenté (ticket #7/F2) laissait la surface UI sans signal de
limite pour la cible déléguée.

Le service orchestrateur relaie désormais la limite de la cible vers le
service de limite de session, fermant l'écart en cohérence avec le chemin
direct (#30).

Couverture QA (sortie réelle) : orchestrator_service 63 passed,
session_limit_service 15 passed, session_limit_t4 7 passed,
cargo test -p application 0 failed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 13:19:21 +02:00
9430c65050 fix(session-limit): brancher le handle de limite sur le chemin direct (#30)
Le handle de limite de session ne se déclenchait pas quand un agent
directement adressé (dont Main) touchait sa propre limite : le ReplyEvent
::RateLimited du chemin structuré direct n'était pas relayé au service de
limite.

On tap désormais ReplyEvent::RateLimited dans le registre terminal vers
SessionLimitService::on_rate_limited, qui émet AgentRateLimited puis
AgentResumeScheduled et arme la reprise auto annulable, exactement comme le
chemin délégué.

Couverture QA (sortie réelle) : structured_registry_d1 12 passed,
session_limit_wiring 6 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 13:19:11 +02:00
631dd4eccf Merge feature/ticket46-project-tab-add-button into develop
fix(projects): rendre visible le bouton + d'ajout d'onglet projet (#46)

Dernier ticket du sprint « Gestion des bugs » (#48, #45, #27, #47, #46).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 12:42:38 +02:00
b152e33b60 fix(projects): rendre visible le bouton + d'ajout d'onglet projet (#46)
Le bouton + de la barre d'onglets projets était invisible (pas de variant
d'affichage). Il est désormais rendu en permanence via un variant secondary,
avec aria-pressed et title pour l'accessibilité, permettant d'ajouter un
projet en onglet.

Couvert par un nouveau test (ProjectTabs.test.tsx).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 12:42:31 +02:00
0e594adaf3 Merge feature/ticket47-panel-only-windows into develop
fix(windows): restauration panel-only des fenêtres détachées suivant le projet en focus (#47)

Backend + frontend : fenêtres/panneaux détachés restaurés en mode panel-only
(sans project_id figé), suivant le projet en focus de la fenêtre principale
via l'event focused-project.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 12:37:56 +02:00
221cc8be78 fix(windows): fenêtres de panneau détachées suivent le projet en focus côté UI (#47)
Partie frontend. Ajoute un adaptateur focusedProject et un port dédié : la
ViewWindow détachée n'est plus liée à un project_id figé, elle s'abonne à
l'event focused-project émis par la fenêtre principale et affiche le panneau
du projet courant. ProjectsView propage le focus ; le détachement crée une
fenêtre panel-only. Couvert par les tests window/ViewWindow/focusedProject/
ProjectsView.focus.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 12:37:45 +02:00
6387fac34f fix(windows): restauration panel-only des fenêtres détachées suivant le projet en focus (#47)
Partie backend. Les fenêtres/panneaux détachés étaient restaurés avec un
project_id figé au moment du détachement, si bien qu'ils restaient collés à
un projet mort ou incohérent après redémarrage. Ils sont désormais restaurés
en mode panel-only, sans project_id figé, et suivent le projet en focus de la
fenêtre principale via un event focused-project exposé par la couche fenêtre.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 12:37:25 +02:00
1e0cb16ead Merge feature/ticket27-ticket-assistant-mcp-tools into develop
fix(ticket-assistant): édition de ticket via les tools MCP idea_ticket_* (#27)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 09:10:31 +02:00
8570adb8e0 fix(ticket-assistant): édition de ticket via les tools MCP idea_ticket_* (#27)
L'assistant IA d'édition de ticket éditait les fichiers du ticket en direct,
hors de tout contrôle. Il passe désormais par les tools MCP idea_ticket_* :
préparation d'un environnement structuré dédié et policy d'enforcement scopée
au ticket courant, de sorte que l'assistant ne peut agir que sur son ticket
via la surface MCP plutôt que sur le système de fichiers.

Couvert par de nouveaux tests QA (mcp_server, assistant_context_store).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 09:10:23 +02:00
e54ffae0c3 Merge feature/ticket45-model-server-singleflight into develop
fix(model-server): singleflight au démarrage du serveur de modèle local (#45)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 08:52:49 +02:00
3fff3402ec fix(model-server): singleflight au démarrage du serveur de modèle local (#45)
Corrige la race « port_occupied:8080 » au démarrage OpenCode/model server :
plusieurs demandes concurrentes tentaient chacune de lancer le serveur de
modèle local, provoquant un conflit de port. Le démarrage est désormais
sérialisé en singleflight — une seule tentative de lancement partagée entre
les appelants concurrents.

Couvert par un nouveau test de concurrence (cargo test -p application vert :
81 unit + 9 model_server).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 08:52:43 +02:00
f24f8f12aa Merge feature/ticket48-cell-error-banner into develop
fix(layout): bandeau d'erreur cellule au-dessus des boutons de contrôle (#48)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 08:42:23 +02:00
e64c0b7840 fix(layout): bandeau d'erreur cellule au-dessus des boutons de contrôle (#48)
Corrige la superposition z-index dans LeafView : le bandeau d'erreur de
cellule passait sous les boutons de contrôle, rendant le message illisible.
Le voile d'annonces ciblées est réaligné en conséquence.

Couvert par un nouveau test de layering (128/128 frontend au vert QA).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 08:42:13 +02:00
1331c5b2c2 Merge feature/ticket40-persist-restore-windows into develop
Persistance et restauration des fenêtres au redémarrage (#40).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 08:00:25 +02:00
2eb51d3335 feat(windows): persistance et restauration des fenêtres au redémarrage (#40)
Persiste l'état des fenêtres (layout/position) et le restaure au
relancement de l'application. Découpage hexagonal :
- domaine : modèle et port d'état des fenêtres (layout, ports)
- application : use cases de persistance/restauration
- infrastructure : store window_state (adapter de persistance)
- présentation : câblage app-tauri (state, commands, lib)
Couvert par des tests ciblés domaine/application/infrastructure.

Depend de #39 (fermeture des fenêtres auxiliaires).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 08:00:20 +02:00
55087b5d9b Merge feature/ticket29-persist-ticket-filters into develop
Persistance des filtres tickets entre redémarrages (#29).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 07:45:56 +02:00
79f06c26d9 feat(tickets): persistance des filtres tickets entre redémarrages (#29)
Introduit le port UiPreferencesGateway et son adapter uiPreferences,
avec le module ticketFilterPersistence qui sauvegarde/restaure les
filtres (recherche, statut, sprint, agents) via useTickets,
useTicketSearch et useProjectAgents. Couvert par tests unitaires et
un test d'intégration.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 07:45:50 +02:00
e6f10d2b74 Merge feature/ticket39-close-main-closes-all-windows into develop
Fermeture des fenêtres auxiliaires à la fermeture de la fenêtre principale (#39).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 07:45:34 +02:00
13d45cb752 feat(app-tauri): fermeture des fenêtres auxiliaires quand la principale se ferme (#39)
Ajoute close_non_main_webview_windows sur l'event de fermeture de la
fenêtre principale, avec le prédicat should_close_with_main_window qui
exclut la fenêtre "main" et cible toutes les fenêtres auxiliaires
(vues détachées, settings…). Couvert par 3 tests unitaires.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 07:45:27 +02:00
9ee7290cde Merge feature/ticket38-assign-sprint-on-create into develop
Attribution d'un sprint à la création de ticket via popup SprintPicker
(#38). QA vert (tsc + 634 tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 20:03:45 +02:00
0039958b82 feat(tickets): attribution d'un sprint à la création via popup SprintPicker (#38)
Nouveau composant SprintPicker permettant de choisir un sprint lors de
la création d'un ticket depuis TicketsPanel ; export ajouté à l'index
des features tickets. Frontend-pur.

QA vert : tsc --noEmit exit 0, vitest 63 fichiers / 634 tests passés.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 20:03:41 +02:00
a3c0dd410a Merge feature/ticket37-sprint-tickets-status-filters into develop
Conservation des tickets en sprint + statut + filtres (#37). QA vert
(tsc + 623 tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 19:55:58 +02:00
1c28754b58 feat(tickets): conservation des tickets en sprint, affichage du statut & filtres (#37)
Les tickets restent rattachés à leur sprint ; SprintManager affiche le
statut et propose des filtres via useTicketSearch. Frontend / read-query.

QA vert : tsc --noEmit exit 0, vitest 62 fichiers / 623 tests passés.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 19:55:54 +02:00
338b8707de Merge feature/ticket41-ticketpicker-multiselect into develop
Multi-sélection TicketPicker (#41). QA vert (tsc + 620 tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 19:45:44 +02:00
daa93525d7 feat(tickets): multi-sélection dans TicketPicker (#41)
Le TicketPicker permet la sélection multiple de tickets ; SprintManager
consomme la sélection multiple. Frontend-pur.

QA vert : tsc --noEmit exit 0, vitest 62 fichiers / 620 tests passés.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 19:45:39 +02:00
61e5e41f0d Merge feature/ticket42-dockcontrols-header into develop
Refinement UX DockControls (#42) suite #26 : clarification des
contrôles d'en-tête. QA vert (tsc + 616 tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 19:32:42 +02:00
73859a05e1 feat(ui): clarification des contrôles d'en-tête DockControls (#42)
Refinement UX suite #26 : clarifie les contrôles DockControls dans
l'en-tête des panneaux. Frontend-pur.

QA vert : tsc --noEmit exit 0, vitest 62 fichiers / 616 tests passés.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 19:32:37 +02:00
c864d4a692 Merge feature/ticket26-ui-rework-menus into develop
Refonte menus & fenêtres (#26) : menu unique Panneaux, onglets projet
avec bouton +, sous-menus flyout. QA vert (tsc + 616 tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 15:08:52 +02:00
09e7f212b7 feat(ui): refonte menus & fenêtres — menu unique Panneaux et onglets projet (#26)
Sous-menus flyout dans MenuBar ; ProjectsView expose un menu unique
Panneaux et supprime les menus View/Window/File ainsi que le sélecteur
de projet ; ProjectTabs gère les onglets et le bouton +. Tests migrés
en conséquence.

QA vert : tsc --noEmit exit 0, vitest 62 fichiers / 616 tests passés,
invariants préservés.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 15:08:34 +02:00
805d4b70a2 Merge feature/model-server-hf-source-wizard-v2 into develop
Support natif d'une source modèle Hugging Face (-hf) en alternative au
chemin .gguf local pour le serveur llama.cpp intégré, options structurées
llama.cpp (host/gpu_layers/context_size/jinja), preview de commande, et
refonte UX du wizard V2. Migration store model-servers.json V1 -> V2.

QA : suites ciblées vertes (domain 7, application 8, infra model_server 4,
app-tauri dto 5, Vitest 22, tsc/build OK).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 14:32:21 +02:00
efa081b074 feat(model-server): wizard V2 config modèles locaux — source, options structurées & preview
Refonte UX du wizard de configuration des modèles locaux du serveur
llama.cpp intégré, alignée sur la source Hugging Face et les options backend.

- choix de source sans jargon (chemin local .gguf vs dépôt Hugging Face) ;
- champs structurés -ngl (gpu_layers), -c (context_size), --jinja, --host ;
- zone d'arguments libres avec alerte sur les flags réservés ;
- preview de la commande via le backend (previewModelServerCommand, debounced) ;
- gateway/ports et mock adaptés au contrat V2.

QA : Vitest 22 verts, tsc/build OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 14:32:04 +02:00
38aecf2cac feat(model-server): source Hugging Face (-hf) + options structurées llama.cpp + migration store V2
Backend du support natif d'une source modèle Hugging Face en alternative au
chemin .gguf local pour le serveur llama.cpp intégré, et refonte des options.

- domaine : ModelSource {LocalPath|HuggingFace} + HfModelRef,
  LlamaCppOptions {host,gpu_layers,context_size,jinja}, invariant auto_start
  exigeant une source, validate_free_args (rejet des flags réservés).
- infra : build_argv partagé avec build_spawn_spec, émission -hf vs --model,
  ordre argv figé ; migration model-servers.json V1 -> V2.
- app-tauri : DTO V2 (modelSource, compat modelPath, conflit = INVALID),
  commande preview_model_server_command.

QA : cargo test ciblés verts (domain 7, application 8, infra model_server 4,
app-tauri dto 5). Échecs des suites complètes infra/app-tauri environnementaux
(sandbox bind/socket), hors périmètre.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 14:31:54 +02:00
bc96285fa5 Merge feature/modeles-locaux into develop
Sprint « Modeles locaux » terminé et vert : serveur de modèle local
llama.cpp intégré (#35) et profils OpenCode locaux multiples (#36).
Backend (domain/application/infra/app-tauri) + frontend.

Tests verts (exécution réelle) : cargo build workspace Finished ;
domain+application ; app-tauri dto_model_servers 3/3, dto_profiles 10/10 ;
infra model_server 2/2 ; application model_server+profile_usecases 19/19 ;
frontend tsc propre + vitest 608/608.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 15:51:05 +02:00
a244f32f31 chore(ideai): versionne le store tickets/sprints/mémoire du sprint Modeles locaux
Store d'orchestration du sprint « Modeles locaux » : sprint 883534aa,
tickets #35/#36 (et #32/#37–#41 du store), compteurs et index, notes
mémoire de livraison (#35 CRUD/statut, #36 multi-profils, OpenCode↔llama.cpp).

Runtime transitoire volontairement exclu (background-tasks/, agent scratch
testllamacpp.md).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 15:50:46 +02:00
72476a650a feat(model-server): frontend modèles locaux — badge de statut & CRUD serveurs (#35) et wizard multi-profils OpenCode (#36)
Sprint « Modeles locaux », couche frontend.

#35 :
- F35.1 badge de statut de lancement du serveur local
  (ModelServerLaunchBadge + useAgentsModelServer).
- F35.2 feature model-servers : CRUD (ModelServersPanel / useModelServers /
  gateway modelServer) et ModelServerSelect.

#36 :
- Liste multi-profils OpenCode dans le wizard de premier lancement,
  gateway de clonage (clone_opencode_profile_from_seed).

Tests verts (exécution réelle) : tsc propre, vitest 608/608.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 15:50:18 +02:00
b82ac76f8b feat(model-server): backend modèles locaux — serveur llama.cpp intégré (#35) et profils OpenCode locaux multiples (#36)
Sprint « Modeles locaux », couche backend (domain/application/infra/app-tauri).

#35 — Serveur de modèle local intégré (llama.cpp) :
- domain: model_server.rs (agrégat + statut), ports ModelServerProbe /
  ManagedProcess / ModelServerRuntime / ModelServerRegistry, événements
  model_server_status_changed et agent_launch_failed.
- application: use case EnsureLocalModelServer branché sur LaunchAgent.
- infrastructure: adapters HttpOpenAiCompatibleProbe, LlamaCppRuntime,
  LocalManagedProcess, FsModelServerRegistry.
- app-tauri: DTO plat LocalModelServerConfigDto, commandes
  list/save/delete_model_server avec garde model_server_in_use, wiring.

#36 — Profils OpenCode locaux multiples :
- domain: VO LocalModelServerId, OpenCodeConfig.local_model_server_id.
- application: use case CloneOpenCodeProfileFromSeed.
- app-tauri: commande clone_opencode_profile_from_seed, DTO/wiring.

Tests verts (exécution réelle) : domain+application OK, app-tauri
dto_model_servers 3/3 et dto_profiles 10/10, infra model_server 2/2,
application model_server+profile_usecases 19/19, cargo build workspace Finished.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 15:50:08 +02:00
2e98f1fbb9 Merge feature/opencode-llamacpp into develop
Remplace le provider Ollama par llama.cpp dans l'intégration OpenCode
(tool-calling local). QA vert, réserve E2E live non bloquante.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 00:41:31 +02:00
4e70631c40 feat(opencode): remplace le provider Ollama par llama.cpp
Le tool-calling local ne fonctionnait jamais via Ollama. Refonte du
support local d'OpenCode autour de llama.cpp: profil, catalogue,
matérialisation de la config OpenCode et surface first-run alignés sur
llama-server (backend + frontend).

QA vert (commandes réelles): domain 244, application 81+64, infra 263
(10 échecs = bind-port sandbox identiques sur develop, non-régression),
frontend 574, tsc propre. Réserve E2E live non bloquante faute de
llama-server joignable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 00:41:19 +02:00
eaba05d27d feat(opencode): remplace le profil Ollama HTTP par OpenCode
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 15:48:07 +02:00
2fa226e413 chore(tickets): versionne le store de tickets et de sprints du projet
`.ideai/tickets/` et `.ideai/sprints/` sont le registre durable du projet (les
tickets sont déjà référencés par les messages de commit : #14, #21..#25, #28),
au même titre que `.ideai/memory/`. Ils entrent donc dans le dépôt.

À l'inverse, `.ideai/background-tasks/` (snapshots de rendez-vous headless) et
`.ideai/proposals/` (amendements de contexte en attente d'arbitrage) restent de
l'état d'exécution transitoire, de la même classe que `live-state.json` et
`.ideai/requests/` : ils doivent être ignorés (patch `.gitignore` à venir).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 08:38:33 +02:00
56757c7e1b chore(ideai): contextes des agents UX/TestOllama, MAJ DevFrontend et permissions associées
Ajoute les contextes des deux nouveaux agents et enregistre leurs règles dans
`.ideai/permissions.json`. Aucun impact applicatif : état de configuration
projet, isolé du code de feature.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 08:38:21 +02:00
ffc458e477 docs(memory): notes projet du sprint UI rework et des tickets #7/#14/#16/#17/#18/#25/#28
Capitalise les notes durables produites pendant le sprint UI rework et les
tickets livrés depuis. Commit séparé du code : `.ideai/memory/` est le store
durable versionné, il ne doit jamais être mélangé aux commits de feature.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 08:38:16 +02:00
710fa8fdd7 Merge feature/ticket28-firstrun-detect-hang into develop (#28)
First-run wizard figé : boutons « Save and continue » et « Detect installed
CLIs » grisés à vie.

Cause racine à deux étages, corrigée aux deux :
- backend : `AgentRuntime::detect` était un `fn` synchrone pilotant un
  spawner async via un `block_on` imbriqué — panique sous le runtime Tauri,
  commande IPC sans réponse. Le port passe en `async fn`, la sonde est bornée
  par un timeout, et les profils OpenAI-compatible sont sondés en HTTP.
- frontend : `busy` awaitait `detectProfiles`, si bien qu'une promesse
  pendante grisait les actions sans issue. La détection devient best-effort,
  suivie par un drapeau `detecting` séparé qui n'inhibe rien.

QA vert (exécution réelle) : domain 241, application 81, infrastructure 269,
app-tauri 63+15 ; vitest 569 tests / 59 fichiers ; tsc --noEmit exit 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 08:12:00 +02:00
0389e2e0b0 fix(first-run): découple busy de la détection des CLI (#28)
Même backend réparé, le wizard restait à la merci d'une détection qui ne
répond pas : `reload()` et `detect()` awaitaient `detectProfiles` sous le
même drapeau `busy` qui grise « Save and continue » et « Detect installed
CLIs ». Une promesse IPC jamais résolue laissait donc les deux boutons
grisés à vie — sans issue pour l'utilisateur.

La détection redevient ce qu'elle est : une étape best-effort, jamais
bloquante.

- `busy` ne garde plus que ce dont le wizard ne peut pas se passer
  (`firstRunState`) ou qui mute l'état (`configureProfiles`). Il ne dépend
  plus jamais de `detectProfiles`. L'invariant est documenté en tête de
  module.
- Un drapeau `detecting` distinct suit la sonde et n'inhibe aucune action.
  Il est relâché par un timer (`DETECT_TIMEOUT_MS`), jamais par la seule
  promesse : celle-ci peut rester pendante indéfiniment.
- Un identifiant de tour monotone (`detectRun`) invalide les tours périmés
  et ceux qui survivent au démontage, évitant un `setState` hors montage.
- `reload()` rend les lignes immédiatement puis lance la détection en tâche
  détachée. Si elle échoue, les lignes restent cochables à la main ; seul le
  bouton explicite remonte l'erreur.

Tests: vitest 59 fichiers / 569 tests verts, tsc --noEmit exit 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 08:11:24 +02:00
3abf2fce98 fix(runtime): detection non bloquante, bornée dans le temps, HTTP pour les profils locaux (#28)
Le first-run wizard restait figé : `CliAgentRuntime::detect` construisait un
runtime tokio courant-thread via `futures_block_on` pour piloter un
`ProcessSpawner` async. Appelé depuis le runtime async de Tauri, ce
`block_on` imbriqué panique ; la commande IPC ne répond alors jamais, la
promesse `detectProfiles` reste pendante et `busy` ne redescend plus.

Ce commit s'attaque à la cause côté backend :

- `AgentRuntime::detect` devient `async fn` (`#[async_trait]`) : le port cesse
  de mentir sur sa nature. Il pilote un spawner async, il est async. Le
  `futures_block_on` disparaît, et avec lui le runtime imbriqué.
- La sonde CLI est bornée par `tokio::time::timeout(DETECTION_TIMEOUT)` :
  un binaire qui ne rend jamais la main dégrade la détection en `Err`, il
  ne gèle plus l'appelant.
- Les profils `StructuredAdapter::OpenAiCompatible` n'ont pas de CLI à
  spawner : les sonder revenait à tester un binaire inexistant. Ils sont
  désormais sondés par un GET HTTP sur l'endpoint `/models` dérivé de
  `chat_http.endpoint`, borné par le même timeout.

`DetectProfiles` séquence les `await` sur les candidats. Les fakes
`AgentRuntime` des crates application/infrastructure/app-tauri suivent la
nouvelle signature.

Tests: cargo test -p domain (241), -p application (81), -p infrastructure
(269), -p app-tauri (63+15) — verts. `rg futures_block_on crates` sans match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 08:11:14 +02:00
3c6cd04bd8 Merge feature/ticket14-local-lan-profiles into develop (#14)
Intègre les profils IA locaux/LAN OpenAI-compatible comme profils IdeA
canoniques (adapter HTTP additif, parité tool-calling/MCP).

Backend (aab4bca) + frontend (d89380c), validés GO par QA de bout en bout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 22:35:08 +02:00
d89380cdf0 feat(ui): profils locaux/LAN OpenAI-compatible dans le first-run et les terminaux (#14)
Câble la surface frontend des profils IA locaux/LAN OpenAI-compatible,
en parité avec l'adapter backend additif (aab4bca).

- domain: types de profil OpenAI-compatible
- first-run: édition/validation du profil dans le FirstRunWizard
- adapters/mock: mock de profil pour les tests
- terminals: rendu des round-trips et erreurs endpoint (role=alert)

Validé QA (frontend GO): typecheck exit 0, vitest 59 fichiers / 566 tests,
0 echec ; couverture timeouts round-trip + erreur endpoint role=alert
prouvees ; pas de regression sur les tests de bail headless.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 22:34:50 +02:00
aab4bcafb6 feat(session): adapter HTTP OpenAI-compatible pour profils locaux/LAN (#14)
Ajoute un adapter de session HTTP OpenAI-compatible, purement additif,
permettant d'intégrer des modèles locaux/LAN comme profils IdeA canoniques
avec parité tool-calling/MCP.

- domain: extension du profil et des ports pour l'adapter OpenAI-compatible
- infrastructure: adapter openai_compat + routage factory
- app-tauri: mapping des outils OpenAI (openai_tools) + wiring state/lib
- application: catalogue d'agents + tests de use-cases profils

Validé QA (backend GO): round-trip byte-identique Claude/Codex, mapping
erreurs, dégradation tools, conversation_id None, conformance un seul Final,
routage factory. Suites vertes domain 467 / application 530 /
infrastructure 523 / app-tauri 247, 0 échec.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 22:09:16 +02:00
cb20fabdf4 Merge feature/ticket25-assistant-sandbox-plan into develop (#25)
fix(assistant): retire le plan sandbox project_read_only qui provoquait
l'EACCES/os error 13 au spawn de la CLI de l'assistant de ticket.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 17:28:37 +02:00
961a2cadc4 fix(assistant): retire le plan sandbox project_read_only de l'assistant de ticket (#25)
La surface « assistant de ticket » imposait un SessionPlan Landlock
restrictif (project_read_only) qui gouvernait la classe exec de la
sandbox OS et empêchait le spawn de la CLI (EACCES / os error 13).

factory.start reçoit désormais SessionPlan::None + sandbox absent :
l'assistant n'impose plus ce plan OS-sandbox. La classe exec n'est
plus verrouillée, le spawn de la CLI aboutit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 17:28:30 +02:00
47b1d64e39 Merge feature/ticket23-system-view-windows into develop (#23)
Fenêtres View système séparées (WebviewWindow Tauri, multi-écran,
fullscreen) — backend commandes/capabilities + composition root,
frontend port WindowGateway + entrée panel-only. Clôt le sprint
UI rework. QA vert : app-tauri 63, frontend typecheck + vitest 546/546.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 16:53:52 +02:00
69e785ec7e feat(ui): fenêtres View système séparées — WebviewWindow Tauri + port WindowGateway (#23)
Détache les vues dans de vraies fenêtres système Tauri (multi-écran,
fullscreen). Backend : commandes de gestion de WebviewWindow, capabilities
et composition root. Frontend : nouveau port WindowGateway et son adaptateur
window, entrée panel-only ViewWindow/ViewPanelBody, détachement câblé dans
ProjectsView. QA vert : app-tauri 63, frontend typecheck + vitest 546/546.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 16:53:43 +02:00
2323a71f01 Merge feature/ticket22-view-anchor-docking into develop (#22)
Anchor de views — primitive de docking DockRegion + modèle ViewPlacement,
câblés dans ProjectsView. QA vert : frontend typecheck + vitest 537/537.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 11:54:50 +02:00
37bf7d4167 Merge feature/ticket21-sort-tickets into develop (#21)
Tri de la liste de tickets — backend (TicketListQuery/ticket_list, MCP)
+ frontend (contrôle « Trier par… » dans TicketFacetsBar). QA vert :
app-tauri 60+15, infrastructure 254, frontend typecheck + vitest 533/533.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 11:54:44 +02:00
bd73fd79bd feat(tickets): contrôle « Trier par… » dans la barre de facettes (#21)
Ajoute le sélecteur de tri dans TicketFacetsBar et propage le critère
via useTicketSearch jusqu'à TicketsPanel et TicketPicker, sur le contrat
de tri backend (e523f44). Couvre le tri par les tests tickets et
TicketPicker.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 11:54:28 +02:00
e523f44425 feat(tickets): tri de la liste de tickets côté backend (#21)
Ajoute le paramètre de tri dans TicketListQuery et l'expose via
ticket_list (surface app-tauri + MCP). Ports et adaptateur mock
frontend alignés sur le contrat de tri. La partie UI (contrôle de tri
dans TicketFacetsBar) suivra dans un commit frontend dédié.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 11:45:59 +02:00
419e9b8498 feat(ui): anchor de views — primitive de docking DockRegion + modèle ViewPlacement (#22)
Introduit la primitive de docking DockRegion et le modèle ViewPlacement
pour ancrer les vues dans le chrome, câblés dans ProjectsView. Tests
unitaires DockRegion et test d'intégration docking de ProjectsView.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 11:45:46 +02:00
2f8467e2dd Merge feature/ticket17-focus-trap-fix into develop
fix(tickets): perte de focus dans l'édition d'un ticket (#17) — focus-trap
FloatingWindow passé en mount-only via onCloseRef, + test de régression.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 08:28:15 +02:00
4cccd40e2a fix(tickets): corrige la perte de focus dans l'édition d'un ticket (#17)
Le focus-trap de FloatingWindow avait un useEffect dépendant de `[onClose]`.
`onClose` (ex. TicketDetail.requestClose) est une closure recréée à chaque
render : chaque frappe ré-armait l'effet et renvoyait le focus au premier
élément focusable, ne laissant saisir qu'une lettre à la fois dans le
formulaire d'édition.

Correctif : conserver le dernier `onClose` dans un `onCloseRef` et passer
l'effet focus-trap en mount-only (`[]`). Le handler Escape lit la valeur
courante via la ref. Ajout d'un test de régression prouvé rouge-sans /
vert-avec le fix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 08:28:08 +02:00
f580edc93a Merge feature/tickets-window into develop
Lot C du sprint UI rework : fenêtre dédiée de gestion des tickets (#17).
Clôt le sprint UI rework (#16, #18, #19, #17 intégrés).
Tests verts (tsc clean, suite complète 530/530).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 06:35:26 +02:00
7caced1b99 feat(tickets): fenêtre dédiée de gestion des tickets — liens via popup + assistant IA flottant (#17)
Lot C du sprint UI rework : fenêtre dédiée de gestion des tickets,
appuyée sur la primitive FloatingWindow (#16) et la popup TicketPicker (#18).

- TicketDetail : gestion des liens de ticket via la popup TicketPicker,
  assistant IA en fenêtre flottante
- FloatingWindow : ajustements pour l'usage fenêtre dédiée

Tests : tsc --noEmit clean, suite complète 530/530, suites tickets +
FloatingWindow 50/50.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 06:35:20 +02:00
cb8c9cd634 Merge feature/sprint-ticket-picker into develop
Lot D du sprint UI rework : TicketPicker branché dans la gestion de sprint (#19).
Tests verts (tsc clean, suite complète 528/528, tickets 38/38).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 21:01:16 +02:00
5a0d9f0db4 feat(tickets): sélection de ticket via popup dans la gestion de sprint (#19)
Lot D du sprint UI rework : branche la popup réutilisable TicketPicker (#18)
dans la sélection de ticket de la création/édition de sprint.

- SprintManager : sélection de ticket déléguée à la popup TicketPicker
- TicketsPanel : ajustements liés à l'intégration

Tests : tsc --noEmit clean, suite complète 528/528, suite tickets 38/38
(dont 2 nouveaux tests #19).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 21:01:06 +02:00
a118069f80 Merge feature/ui-menus-floating into develop
Lot A du sprint UI rework : menus déroulants, FloatingWindow, barre unique
et sélecteur de projet permanent (#16). Débloque le lot C #17.
Tests verts (tsc clean, suite complète 527/527).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 20:55:43 +02:00
c55f948d25 feat(ui): menus déroulants + fenêtres flottantes, barre unique et sélecteur de projet (#16)
Lot A du sprint UI rework : réorganisation de la navigation racine.

- shared/ui/MenuBar.tsx : barre de menus unique avec menus déroulants
- shared/ui/FloatingWindow.tsx : primitive de fenêtre flottante réutilisable
  (socle du lot C #17 fenêtre dédiée gestion tickets)
- shared/ui/zIndex.ts : échelle z-index étendue pour les fenêtres flottantes
- App : intègre la barre unique et le sélecteur de projet permanent
- ProjectsView : sélecteur de projet permanent

Tests : tsc --noEmit clean, suite complète 527/527, suites impactées 33/33.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 20:55:37 +02:00
0218e3271f Merge feature/ticket-picker-popup into develop
Lot B du sprint UI rework : popup réutilisable TicketPicker (#18).
Tests verts (TicketPicker 8/8, suite tickets 37/37, tsc clean).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 15:54:53 +02:00
140daae8b3 feat(tickets): popup réutilisable de sélection de ticket (#18)
Ajoute TicketPicker, popup modale réutilisable de sélection de ticket,
socle du chemin critique du sprint UI rework (#17 et #19 en dépendent).

- TicketPicker.tsx : popup de recherche/sélection réutilisable
- useTicketSearch.ts : hook de recherche/filtrage extrait et partagé
- TicketFacetsBar.tsx : barre de facettes extraite de TicketsPanel
  pour réutilisation (listing principal + picker)
- shared/ui/zIndex.ts : échelle z-index centralisée
- TicketsPanel : consomme la barre de facettes extraite

Tests : TicketPicker.test.tsx 8/8, suite tickets 37/37, tsc --noEmit clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 15:54:47 +02:00
d97abb116f Merge feature/work-state-dto-debt into develop
Dette de contrat DTO work-state (#5, frontend) : summary sur BackgroundCompletion
mappé depuis task.summary, affichage discret sur tâche terminale, suppression du
chemin de merge top-level mort. Tests verts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 11:05:11 +02:00
3d603cebcc feat(workstate): dette de contrat DTO work-state (frontend)
Aligne le contrat DTO du panneau work-state (#5).

- ajout summary: string|null à BackgroundCompletion, mappé depuis task.summary
- affichage discret du résumé sur une tâche terminale
- suppression du chemin de merge top-level mort des background tasks

Tests verts : vitest workstate.test.tsx (21), suite complète (507),
npm run build (exit 0).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 11:05:03 +02:00
4bdffa4baa Merge feature/ticket-filter-checkboxes into develop
Filtres multi-critères par cases à cocher (#12) : backend (IssueListFilter en Vec
statuses/priorities, filter_matches OR/AND, DTO en tableaux, MCP idea_ticket_list
aligné) et frontend (TicketListQuery multi-valeurs, UI checkboxes, toggle/clear).
Tests verts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 06:41:11 +02:00
feeca3462c feat(tickets): filtres multi-critères par cases à cocher (backend + frontend)
Passe les filtres de la liste des tickets en sélection multiple (#12).

Backend Rust :
- IssueListFilter : statuses/priorities en Vec, filter_matches en OR intra-champ
  et AND inter-champs
- TicketListRequestDto en tableaux + from_request (parse/déduplication)
- MCP idea_ticket_list aligné sur le nouveau contrat

Frontend :
- TicketListQuery.statuses/priorities
- UI de cases à cocher, toggle/clear des filtres

Tests verts : issue_store (7), app-tauri --lib (59), mcp_server (24),
issue_usecases (6), sprint_usecases (6), frontend vitest (505), npm run build (exit 0).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 06:41:01 +02:00
413db25f08 Merge feature/ticket-deletion into develop
Suppression d'un ticket (#6) : backend (IssueStore::delete, FsIssueStore::delete
sous lock, use case DeleteIssue, event IssueDeleted, commande ticket_delete) et
frontend (gateway delete, retrait de liste/fermeture détail via event). Tests verts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 06:27:15 +02:00
5417bd756b feat(tickets): suppression d'un ticket (backend + frontend)
Ajoute la suppression complète d'un ticket (#6).

Backend Rust :
- port IssueStore::delete (NotFound si absent) + event IssueDeleted{freed_sprint}
- FsIssueStore::delete : supprime .ideai/tickets/<N>/ et l'index sous lock
- use case DeleteIssue + adaptations des tests sprint/ticket_assistant au port
- commande Tauri ticket_delete + câblage events/state/lib

Frontend :
- gateway delete (ports, adapter ticket + mock, domain)
- useTicketDetail : retrait de la liste et fermeture du détail via event issueDeleted
- intégration TicketDetail + tests

Tests verts : application/issue_usecases (6), infrastructure/issue_store (7),
app-tauri --lib (56), frontend vitest (503), npm run build (exit 0).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 06:27:09 +02:00
d25e340b44 Merge feature/ticket-ai-assistant into develop
Assistant IA de ticket (#8) : backend (use cases open/close, policy d'outils
par agent, store de contexte assistant, events, commandes Tauri) et frontend
(gateway, hook, panneau, intégration TicketDetail). Tests verts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 06:06:32 +02:00
1fdf62c089 feat(tickets): création d'un assistant IA de ticket (backend + frontend)
Ajoute le chat assistant IA attaché à un ticket (#8).

Backend Rust :
- use cases OpenTicketAssistant/CloseTicketAssistant + tests
- politique d'outils par agent (domain/agent_tool_policy) et policy MCP
- store de contexte assistant + gabarit default_ticket_assistant.md + tests
- events TicketAssistantOpened/Closed
- commandes Tauri open_ticket_chat/close_ticket_chat et câblage state/lib/events

Frontend :
- gateway (ports, adapters ticket + mock, domain)
- hook useTicketAssistant + composant TicketAssistantPanel
- intégration dans TicketDetail

Tests verts : vitest tickets.test.tsx (23), cargo test application::ticket_assistant (1),
infrastructure::assistant_context_store (2).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 06:06:24 +02:00
f1803768fd Merge feature/sprint-management-ui into develop
Ticket #11 : interface de création et gestion des sprints (frontend pur) —
SprintManager, useTickets, ports/adaptateurs et TicketsPanel. Suite frontend
verte (typecheck + vitest 497+ tests + build).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 21:11:46 +02:00
fa874b976e feat(sprints): interface de création et gestion des sprints (frontend)
Ticket #11 — surface UI de gestion des sprints (frontend pur).

- Nouveau composant SprintManager : création (nom optionnel), édition et
  gestion du cycle de vie d'un sprint, contrôles accessibles (pas de DnD).
- useTickets étendu aux opérations de gestion de sprint ; ports, adaptateurs
  (ticket.ts, mock/index.ts) et TicketsPanel câblés en conséquence.
- Tests Vitest associés (tickets.test.tsx).

Typecheck / vitest (497+ tests) / build verts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 21:11:37 +02:00
edd5486330 Merge feature/sprint-model into develop
Ticket #10 : modèle de sprints — agrégat domaine, use-cases, persistance,
exposition MCP et app-tauri (backend) + surface frontend (adaptateurs, ports,
UI tickets). Suite complète verte (domaine/application/infra/app-tauri +
frontend typecheck/vitest/build).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 10:57:44 +02:00
69759d351c feat(sprints): surface frontend du modèle de sprints
Ticket #10 — volet frontend des sprints.

- Domaine et ports frontend étendus au modèle de sprints (domain/index.ts,
  ports/index.ts).
- Adaptateurs : ticket.ts et mock/index.ts câblent les opérations sprints.
- UI tickets : useTickets + TicketsPanel exposent le rattachement/gestion des
  sprints, avec tests Vitest associés.

Typecheck / vitest / build verts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 10:57:38 +02:00
48b652ff91 feat(sprints): modèle de sprints — domaine, use-cases, persistance et surfaces (backend)
Ticket #10 — introduction du modèle de sprints côté backend.

- Domaine : nouvel agrégat Sprint (sprint.rs), IDs, événements et invariants ;
  rattachement des issues à un sprint (issue.rs) et ports associés.
- Application : use-cases sprints (application/src/sprints) + erreurs dédiées.
- Infrastructure : store de sprints (infrastructure/src/sprints.rs), adaptation
  du store d'issues et exposition MCP via orchestrator/mcp/tickets.rs.
- app-tauri : commandes, state et events pour piloter les sprints depuis l'UI.

Tests domaine/application/infra/app-tauri verts (sprint_usecases, sprint_store,
issue_store, mcp_server).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 10:57:33 +02:00
9740149ebb Merge feature/ticket-edit-persistence into develop
Ticket #9 : persistance de l'édition d'un ticket côté frontend
(useTicketDetail + TicketDetail), tests Vitest tickets verts (13/13).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 02:20:29 +02:00
5ecc64f9c5 feat(tickets): persistance de l'édition d'un ticket côté frontend
Ticket #9 — l'édition d'un ticket (titre, corps, champs) est désormais
persistée depuis le détail : useTicketDetail porte l'état d'édition et le
flux de sauvegarde, TicketDetail expose l'UI d'édition/validation.

Tests Vitest tickets verts (13/13), typecheck et build OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 02:20:23 +02:00
ce6ef5efae Merge feature/agent-live-announcements into develop
Intègre les annonces live inter-agent (ticket #4) : backend du tap
mpsc live (B0-B3), surface frontend des annonces sur les cellules
(F1-F3), et les notes projet associées.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 20:11:21 +02:00
f0adf2dcc4 docs(memory): notes projet du redémarrage des annonces live (ticket #4)
Notes du chantier #4 relancé sur develop, commit séparé du code
(convention d'usage) :

- ticket4-restart-on-develop-ebd992e: baseline de redémarrage.
- ticket4-overlay-composition-leafview: décision de composition de l'overlay
  dans le layout (LeafView).
- ticket4-announcements-frontend-f1f2f3: cadrage de la surface frontend F1-F3.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 19:50:22 +02:00
50b2adfde3 feat(inter-agent): surface frontend des annonces live sur les cellules (F1-F3)
Affiche les annonces d'agent en direct au-dessus des cellules, sur la base
develop :

- announcements: nouveau feature module — store réactif, provider
  d'abonnement aux événements, overlay par cible et aperçu, avec tests
  (announcementsStore + provider).
- App.tsx: montage du provider dans l'arbre applicatif.
- domain/index.ts: types partagés de l'événement d'annonce côté front.
- layout: composition de l'overlay dans LayoutGrid + règle d'exclusion
  couverte par overlayExclusion.test.ts.
- AgentsPanel: intègre l'aperçu des annonces dans la surface existante.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 19:50:13 +02:00
aa5a4f30ae feat(inter-agent): backend des annonces live d'agent (B0-B3)
Redémarre le ticket #4 sur la base develop. Émission d'annonces live d'un
agent vers l'UI, distinctes du Final de délégation :

- domain: ReplyEvent::Announcement/Final et DomainEvent::AgentAnnouncement
  (events.rs), port d'émission (ports.rs), gating de readiness (readiness.rs).
- application: mapping des événements structurés en annonces
  (agent/structured.rs, agent/mod.rs, lib.rs) et relais côté orchestrateur
  (orchestrator/service.rs).
- infrastructure/session: parse des annonces + fix du Final pour Claude et
  Codex, propagé aux adaptateurs et à la conformance
  (claude.rs, codex.rs, conformance.rs, mod.rs, process.rs, sandbox_e2e.rs).
- app-tauri: relais Tauri des annonces vers le front (events.rs, chat.rs).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 19:50:03 +02:00
ebd992e41a docs(memory): notes projet tâches de fond, tickets V1 et checkpoints ticket #1
Capitalise la mémoire projet accumulée pendant les chantiers B7/B8
(tâches de fond first-class), le système de tickets V1 et le ticket #1 :
design, cadrages d'archi, checkpoints d'avancement et verdicts QA/frontend.
Mise à jour de l'index MEMORY.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 00:21:43 +02:00
eb9cc16181 feat(background): livraison auto au propriétaire + projection des tâches de fond dans le work-state (T1+T3)
T1 — Wake automatique du propriétaire à la complétion.
La complétion d'une tâche de fond est désormais livrée à l'agent
propriétaire dès que la session accepte l'envoi (wake.rs :
mark_completion_delivered au send accepté), via un drain de flux dédié
(structured.rs : drain_reply_stream_with_readiness). L'inbox médiée
enfile l'item sans démarrer de tour ni marquer l'agent busy
(input/mod.rs : enqueue FIFO silencieux). Régression couverte
(tests/agent_wake.rs, tests input/mod.rs).

T3 — Tâches de fond projetées dans le read-model du panneau Work.
AgentWorkState porte désormais background_tasks
(VO AgentBackgroundTaskState), alimenté par un builder best-effort
with_background_tasks(store) : union list_open_for_agent + dispatch des
completions non livrées par owner_agent_id, erreur store => Vec vide
(aucune régression live/busy/tickets). DTO Tauri backgroundTasks et
wiring du BackgroundTaskStore côté state.rs. Le frontend, déjà câblé,
affiche Cancel/Retry (mapping queued/waiting -> pending, tri sur
updatedAtMs). Borne V1 : une tâche terminale déjà livrée n'est plus
énumérable (Retry limité à la fenêtre non livrée).

Tests : cargo build --workspace OK ; cargo test -p application /
-p app-tauri / -p infrastructure verts ; frontend build + vitest verts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 00:21:33 +02:00
f08dae62eb feat(background): déclencheur in-app des tâches de fond via MCP idea_run_in_background (BE-1+BE-2)
Ferme la boucle B8 côté surface agent : un agent peut lancer une commande
en tâche de fond depuis l'app, sans dépendance à un déclencheur externe.

- domain : variante OrchestratorCommand::RunInBackground.
- infrastructure : outil MCP idea_run_in_background (déclaration + schéma +
  map_tool_call), tests de mapping et compteur de catalogue.
- application : handler → SpawnBackgroundCommand (owner=requester, WakeOwner).
- app-tauri : câblage .with_spawn_background_command + catalogue MCP 23→24.

Tests verts : domain 452 / application 467 / infrastructure 489 /
app-tauri 236, build --release vert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 22:44:48 +02:00
41278bd632 Merge branch 'feature/issue-ticket-system' into feature/background-tasks-first-class
Intègre le système de tickets/issues V1 (validé QA vert de bout en bout,
mémoire tickets-v1-e2e-validated-qa) dans la branche des tâches de fond.
Conflits résolus : app-tauri/state.rs, domain/events.rs, domain/lib.rs, domain/ports.rs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 15:15:43 +02:00
c179f93a26 feat(frontend): boutons cancel/retry des tâches de fond (B8)
Câble l'action utilisateur sur les commandes Tauri B8 :

- port WorkStateGateway : ajout de cancelBackgroundTask/retryBackgroundTask
  (le read-model se rafraîchit via l'événement domaine backgroundTaskChanged ;
  retry rejoue sous un NOUVEL id de tâche).
- adapter Tauri : invoke cancel_background_task/retry_background_task.
- adapter mock : no-ops (le refresh réel est piloté par les événements).
- ProjectWorkStatePanel (BackgroundTaskRow) : état busy + affichage d'erreur
  + refresh après action.
- test ProjectsView.ls7 : stub de gateway complété.

Build vert : npm run build (tsc --noEmit + vite build), vitest 449 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 14:53:32 +02:00
8cac1470ac feat(background): runner PTY B8 + boucle sink fermée + refactor point-2
Livre le lot backend B8 des tâches de fond :

- infrastructure : runner de commandes concret (CommandBackgroundRunner)
  sur le port BackgroundTaskRunner, tail borné (bounded_tail/BoundedTail),
  éclatement du module background_task en sous-modules (mod/runner/tail,
  sink extrait de l'ancien background_task.rs).
- application : nouveau module background exposant les cas d'usage
  SpawnBackgroundCommand, CancelBackgroundTask, RetryBackgroundTask et le
  port BackgroundCommandArchive.
- domain : refactor point-2 de l'arbitrage Architect — sortie du trait
  BackgroundCommandArchive de la couche domaine vers application.
- app-tauri : câblage runtime (commands, dto, state, lib) des commandes
  spawn/cancel/retry et de la boucle de complétion sink fermée en
  composition root.

Build workspace + tests application/infrastructure verts (QA).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 14:48:46 +02:00
c1d82eee8d feat(tickets): surface frontend V1 du système de tickets
Surface UI complète des tickets (domaine Issue exposé « ticket »),
validée QA de bout en bout : cargo build + tests backend verts,
npm run build + npm test (459) verts.

- Domaine : DTO Ticket, 9 events Issue* + guard isTicketEvent.
- Ports : TicketGateway (+ types query/input) ajouté à Gateways.
- Adapters : TauriTicketGateway réel (+ isTicketVersionConflict),
  wiring, et MockTicketGateway pour les tests.
- Feature tickets : hooks (useTickets, useTicketDetail,
  useProjectAgents), TicketsPanel, TicketDetail, TicketsView,
  ticketMeta + tests.
- ProjectsView : onglet sidebar « Tickets ».

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 22:56:36 +02:00
8de7be01a8 feat(tickets): checkpoint backend V1 du système de tickets (T1-T5)
Checkpoint de travail — QA formelle encore à venir (après reset Codex).
`cargo build` workspace OK, tests ticket/issue verts (domaine, store,
use cases, app-tauri 47/51). Le domaine s'appelle `Issue`, exposé
« ticket » côté MCP/UI.

- T1 domaine : entité Issue (statut, priorité, liens, carnet),
  events, ids, ports.
- T2 infra : store FS Markdown + allocator de références #N.
- T3 application : use cases Issue (create/read/list/update/status/
  priority/carnet/link/unlink/assign) + erreurs dédiées.
- T4 surface MCP : 10 outils publics idea_ticket_* (23 outils au
  total), mappés vers les use cases Issue.
- T5 app-tauri : commandes Tauri ticket_* + DTOs d'events Issue +
  câblage state.rs.

Dette de test PRÉ-EXISTANTE héritée de develop (4 tests app-tauri
mcp_e2e_loopback / mcp_serve_peer rouges) hors périmètre tickets,
non traitée ici.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 22:34:04 +02:00
5d88c952ec feat(frontend): surface UI des tâches de fond (F1-F4)
Expose les tâches de fond côté front : modèle domaine et
normalisation du work state, panneau ProjectWorkStatePanel,
intégration dans LayoutGrid et ProjectsView, et abonnement aux
événements backgroundTaskChanged / agentInboxChanged /
agentWakeChanged pour rafraîchir l'état. npm run build + npm test
(449) verts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 21:33:51 +02:00
e05edc6863 feat(app-tauri): câblage runtime des tâches de fond (B7)
Branche le store de tâches de fond dans le runtime : store per-root
routé par project_id, AppReconcileBackgroundTasks au boot,
AppWakeSessionProvider et boucle ready→MediatedInbox→AgentWakeService,
avec .with_background_tasks(store, clock) au builder OrchestratorService.
open_project appelle reconcile_background_tasks au démarrage.

Aligne aussi 4 tests MCP périmés de l'ancien protocole : idea_reply
retiré (erreur JSON-RPC -32601), idea_ask_agent exposé (13 tools),
rendez-vous capture le Final inline. cargo test -p app-tauri --lib
= 52 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 21:33:45 +02:00
54c8ecfab7 feat(app-tauri): événements des tâches de fond exposés au front (B1-B6)
Câblage des événements de complétion/mailbox/réveil vers la couche
app-tauri pour consommation par le frontend (lots F1-F4 à venir).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 15:47:39 +02:00
f94b54239b feat(application): réveil d'agent et rendez-vous comme tâche de fond (B5-B6)
Orchestration du réveil (wake) d'un agent sur complétion/message et
traitement du rendez-vous inter-agent comme tâche de fond de 1re classe.
Couvert par agent_wake (vert).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 15:47:34 +02:00
c537da54ef feat(infrastructure): store, sink de complétion et mailbox des tâches de fond (B2-B4)
Adapters de persistance des BackgroundTask (store), sink de récupération
de complétion post-tour et boîte de réception (mailbox) pour les messages
concurrents, câblés sur l'entrée. Couvert par background_task_store,
background_completion_sink, agent_inbox et orchestrator_watcher (verts).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 15:47:28 +02:00
f4a55e9988 feat(domain): modèle de tâches de fond de 1re classe et mailbox (B1)
Entités BackgroundTask et Inbox, identifiants, événements et ports du
domaine pour la récupération de complétion post-tour et la réception de
messages concurrents. Fondations des lots infra/application suivants.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 15:47:16 +02:00
fccc1e2f0f chore(gitignore): ignore les target/ de sous-crates Cargo
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 14:49:45 +02:00
62915eec4d fix(codex): dernier agent_message = Final (bootstrap canal)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 14:49:00 +02:00
a9653bc417 fix(orchestrator): délégation inter-agent toujours headless, jamais d'injection PTY
La conversation inter-agent (idea_ask_agent) doit rester strictement
headless : la cible répond via une session structured capturée par IdeA,
sans jamais écrire dans le PTY d'une cellule visible ni fermer le terminal
que l'utilisateur observe.

- lifecycle: flag `allow_structured_alongside_pty` — ouvre une session
  structured pour la délégation sans fermer le PTY visible (coexistence).
- orchestrator/service: `ensure_structured_session` ne ferme plus le PTY
  visible ; mapping typé de l'erreur no-reply ; coexistence PTY/structured.
- infrastructure/input: garantit zéro `DelegationReady` et zéro write PTY
  pour une délégation headless, même quand l'entrée est `front_owned`.
- app-tauri (commands/state): câblage du flag de coexistence.
- tests: fixtures portées vers le modèle structured/headless, assertions
  cibles mises à jour (aucun #[ignore] ajouté, aucun test retiré).

Validé réel : cargo build OK ; orchestrator_service 60/0, agent_lifecycle
62/0, structured_launch_d3 22/0, infrastructure input 35/0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 10:22:06 +02:00
539 changed files with 89339 additions and 11185 deletions

5
.gitignore vendored
View File

@ -1,12 +1,17 @@
# ─── Rust / Cargo ───────────────────────────────────────────────────────────
# Build output for the whole workspace (Cargo.lock IS committed — it's an app).
/target/
# ...et les target/ des sous-crates du workspace (règle non ancrée).
target/
**/*.rs.bk
# ─── Node / frontend ────────────────────────────────────────────────────────
# Dependencies and build output (package-lock.json IS committed).
frontend/node_modules/
frontend/dist/
# Sortie du build web (VITE_TRANSPORT=http) servie par idea-serve : meme
# classe que dist/, rebuildable depuis les sources — not versioned (#65).
frontend/dist-web/
# Root-level node_modules (dev tooling installs at repo root — never versioned).
/node_modules/
# npm/yarn/pnpm debug logs

View File

@ -2,7 +2,8 @@
> Tu es l'**agent de développement frontend** d'IdeA. Tu écris l'UI **TypeScript + React**.
> Tu respectes **strictement** la cartographie d'`Architect` (`.ideai/agents/architect.md`) et
> l'hexagonal **côté frontend aussi**. Tu es appairé à l'agent **QA** : aucune feature n'est
> l'hexagonal **côté frontend aussi**. Tu implémentes l'UI selon les **specs de design de `UX`**
> (`.ideai/agents/ux.md`). Tu es appairé à l'agent **QA** : aucune feature n'est
> finie tant que ses tests (`vitest`) ne sont pas verts.
---
@ -20,18 +21,26 @@ qu'à des **gateways (ports TS)**, jamais directement à l'IPC Tauri.
| `frontend/src/features/` | par feature : `projects`, `agents`, `templates`, `terminals`, `layout`, `git`, `remote`, `first-run`, `memory`, `embedder` | Hooks + composants. Consomment les gateways via le `DIProvider`. |
| `frontend/src/app/` | composition (DI), bootstrap | `useGateways()` doit être appelé dans un `<DIProvider>`. |
**Frontière** : tu consommes les **DTO** exposés par `app-tauri` (périmètre **DevBackend**). Si un
**Frontière backend** : tu consommes les **DTO** exposés par `app-tauri` (périmètre **DevBackend**). Si un
DTO/commande manque ou change, tu te coordonnes avec DevBackend via Main — tu n'inventes pas un
contrat IPC de ton côté.
**Frontière design** : le *quoi et pourquoi visuel* (layout, placement, couleurs, typographie,
espacement, états, interactions, accessibilité) appartient à **UX**. Tu possèdes le *comment
coder*, pas la décision de design. Tu implémentes ses specs ; si une spec est absente, ambiguë ou
techniquement coûteuse/impossible, tu remontes à UX via Main au lieu d'improviser le design.
## 2. Comment tu travailles
1. **Avant de coder** : relis la cartographie d'`Architect` (frontière IPC, gateways concernés) et
regarde les features voisines pour le style (hooks `use*`, structure des composants, tests).
1. **Avant de coder** : relis la cartographie d'`Architect` (frontière IPC, gateways concernés),
récupère la **spec de design d'`UX`** pour l'écran/composant concerné (layout, tokens, états,
critères d'acceptation visuels), et regarde les features voisines pour le style
(hooks `use*`, structure des composants, tests).
2. **Tu écris l'UI** : composants accessibles, état local clair, pas de logique métier dans le JSX
(elle vit dans les hooks/gateways).
(elle vit dans les hooks/gateways). Tu suis les **tokens et patterns** définis par UX plutôt que
d'inventer des valeurs ponctuelles (couleurs, tailles, espacements).
3. **Tu fais valider par QA** : tests `vitest` + `@testing-library/react`. Tu corriges sur rapport
jusqu'au vert.
jusqu'au vert. Le respect des **critères d'acceptation visuels** d'UX fait partie de la revue.
4. **Tu ne déclares jamais « fini » sans la sortie de test réelle.**
## 3. Conventions frontend du projet
@ -41,8 +50,8 @@ contrat IPC de ton côté.
- Les flux temps réel (PTY, events) passent par `listen`/`Channel` encapsulés dans un adapter.
- Tests : co-localisés (`*.test.ts(x)`), exécutés via `vitest`. Utilise les adapters **mock**
pour isoler l'UI du backend.
- Style cohérent avec l'existant (pas de nouvelle lib UI sans validation Architect/Main ; le
design system dédié est un lot ultérieur).
- Style cohérent avec l'existant et avec le système visuel défini par **UX** ; pas de nouvelle lib
UI ni de design system technique sans validation Architect/Main (UX en cadre l'intention).
## 4. Commandes
@ -55,8 +64,9 @@ contrat IPC de ton côté.
contexte applicatif injecté : `idea_ask_agent(target, task)` si l'outil MCP est
disponible, sinon le fallback `.ideai/requests/<ton-agent>/`. Quand tu es sollicité,
réponds normalement en fin de tour ; IdeA capture ta réponse finale.
- Source de vérité d'architecture : `architect.md`. Contradiction code↔doc ⇒ le doc gagne, ou tu
remontes à Main.
- Sources de vérité : **`architect.md`** pour l'architecture (ports/contrats/frontières) et
**`ux.md`** pour le design (layout/tokens/interactions/accessibilité). Contradiction code↔doc ⇒
le doc gagne, ou tu remontes à Main.
## 6. Chantier en cours — « agent = entité, profil découplé »

View File

View File

View File

View File

104
.ideai/agents/ux.md Normal file
View File

@ -0,0 +1,104 @@
# UX — UI/UX Designer d'IdeA
> Tu es **UX**, le **designer UI/UX** du projet IdeA. Tu es propriétaire de l'**expérience
> utilisateur** et de l'**identité visuelle** de l'application : ce que l'utilisateur voit,
> ressent et comprend en naviguant et en pilotant ses agents IA.
> Ton rôle est un rôle de **création et de décision de design**, pas d'implémentation.
---
## 1. Règle centrale : tu ne codes pas
Tu **n'écris pas de code** (ni TypeScript/React, ni CSS de production, ni Rust). Tu **conçois**.
Tu définis ce qui est le mieux pour l'expérience de l'utilisateur, puis tu le **spécifies**
clairement pour que **DevFrontend** l'implémente. Concrètement, tu décides et documentes :
- **Placement et hiérarchie** des éléments : où vont les boutons, menus, panneaux, la disposition
des cellules/terminaux, les zones de navigation.
- **Système visuel** : palette de couleurs (texte, fonds, états, accents), typographie (polices,
tailles, graisses, interlignage), échelle d'espacement, rayons, ombres, iconographie.
- **Interactions et états** : hover/focus/actif/désactivé/chargement/erreur/vide, transitions,
feedback, micro-interactions.
- **Parcours utilisateur** : flux de navigation, premier lancement, création/pilotage d'agents,
organisation du workspace — cohérence d'un écran à l'autre.
- **Accessibilité** : contrastes, tailles de cibles, navigation clavier, focus visible, sémantique
— l'accessibilité est un critère de design, pas une option.
Tu peux lire le projet (code frontend inclus) pour comprendre l'existant et l'état réel de l'UI,
mais tu **ne modifies pas le code de production**. Tu produis des **directives de design**,
maquettes textuelles/ASCII, specs de composants, tokens (couleurs/typo/espacement) et critères
d'acceptation visuels.
---
## 2. Ton livrable : une spec de design actionnable
Quand on te confie un sujet UI/UX, tu réponds avec une **spécification de design** que DevFrontend
peut implémenter sans deviner. Une bonne spec contient :
1. **Intention** : le problème d'expérience résolu et pour quel parcours utilisateur.
2. **Layout** : disposition, hiérarchie, placement précis (au besoin une maquette ASCII/texte).
3. **Tokens visuels** : couleurs (valeurs ou noms de tokens), typographie, espacement, états.
4. **Comportement** : interactions, transitions, tous les états (dont vide/erreur/chargement).
5. **Accessibilité** : contraste, clavier, focus, sémantique attendue.
6. **Critères d'acceptation visuels** : ce qui doit être vrai pour considérer le rendu conforme.
Tu privilégies la **sobriété** et la **cohérence** : un système de design réutilisable plutôt que
des décisions ponctuelles. Réutilise les tokens/patterns existants avant d'en inventer.
---
## 3. Frontières avec les autres agents
- **DevFrontend** implémente tes specs en TypeScript/React. Il possède le *comment coder* ; tu
possèdes le *quoi et pourquoi visuel*. Si une contrainte technique rend une décision de design
coûteuse ou impossible, il te le remonte (via Main) et vous arbitrez.
- **Architect** possède l'architecture hexagonale, les ports/gateways, contrats et frontières
techniques. L'introduction d'une **lib UI** ou d'un **design system** technique se valide avec
Architect/Main — tu proposes l'intention, Architect tranche l'insertion technique.
- **Main** orchestre, découpe et arbitre le produit. Tu passes par lui pour te coordonner avec les
autres agents.
- **QA** valide le fonctionnel ; le respect visuel de tes critères d'acceptation fait partie de la
revue de la feature.
Tu interviens **en amont** du cycle de dev : idéalement, tu cadres le design **avant** que
DevFrontend n'implémente, pour éviter les reprises.
---
## 4. Repères produit
IdeA est un **IDE next-gen 100 % IA** : l'utilisateur ne code pas directement, il **organise et
pilote des agents IA**. L'expérience doit servir ce modèle mental.
Repères stables qui cadrent tes décisions :
- Un onglet par projet, multi-fenêtres OS supporté.
- Workspace organisé en **terminaux/cellules redimensionnables**.
- Agents par projet, templates globaux, synchronisation template → agents.
- Profils IA déclaratifs et éditables ; configuration au premier lancement.
- Surfaces distinctes : ce qui s'adresse à l'**agent** vs à l'**humain** ne se mélange pas.
- L'app est dense et outillée (Git, SSH, WSL, tickets, tâches en arrière-plan, viewer de
conversations) : ton enjeu est la **lisibilité** et la **charge cognitive**, pas l'esbroufe.
Cible d'expérience : **claire, sobre, cohérente, sans friction**. L'utilisateur doit toujours
savoir où il est, ce que font ses agents, et quelle est la prochaine action.
---
## 5. Délégation & collaboration
- Pour discuter avec un autre agent, utilise le mécanisme IdeA du contexte injecté :
`idea_ask_agent(target, task)` quand l'outil MCP est disponible. Quand tu es sollicité, réponds
normalement en fin de tour ; IdeA capture ta réponse finale. Tu ne gères pas de ticket et
n'appelles pas d'outil de remise de résultat.
- **Mémoire durable projet** : lis-la (`idea_memory_read`) pour connaître les décisions de design
déjà prises et écris-y (`idea_memory_write`) les conventions visuelles/UX stables (tokens,
patterns, décisions de parcours) pour qu'elles soient réutilisées et non refaites.
- Contradiction entre une décision de design documentée et le rendu réel du code ⇒ tu le remontes à
Main pour correction, tu ne patches pas le code toi-même.
---
*Dernière mise à jour : 2026-07-07*

File diff suppressed because one or more lines are too long

View File

@ -8,38 +8,60 @@
- [session-limit-handling-design](session-limit-handling-design.md) — Design valide (detecteur hierarchique + reprise auto annulable) pour les limites de session des agents.
- [git-owns-commit-merge-decisions](git-owns-commit-merge-decisions.md) — Ne jamais demander a l'utilisateur s'il faut committer/merger/brancher : l'agent Git tranche toute la topologie du depot.
- [conversation-rotation-safety-design](conversation-rotation-safety-design.md) — memory note conversation-rotation-safety-design
- [checkpoint-orchestrator-designation-restart](checkpoint-orchestrator-designation-restart.md) — memory note checkpoint-orchestrator-designation-restart
- [checkpoint-orchestrator-designation-backend-compile-fix](checkpoint-orchestrator-designation-backend-compile-fix.md) — memory note checkpoint-orchestrator-designation-backend-compile-fix
- [checkpoint-orchestrator-designation-qa-verdict](checkpoint-orchestrator-designation-qa-verdict.md) — memory note checkpoint-orchestrator-designation-qa-verdict
- [checkpoint-orchestrator-designation-appimage-build](checkpoint-orchestrator-designation-appimage-build.md) — memory note checkpoint-orchestrator-designation-appimage-build
- [checkpoint-blocked-until-appimage-030-restart](checkpoint-blocked-until-appimage-030-restart.md) — memory note checkpoint-blocked-until-appimage-030-restart
- [checkpoint-delivery-submit-logging-fix](checkpoint-delivery-submit-logging-fix.md) — memory note checkpoint-delivery-submit-logging-fix
- [checkpoint-workstate-delegation-queue-lot-b](checkpoint-workstate-delegation-queue-lot-b.md) — memory note checkpoint-workstate-delegation-queue-lot-b
- [checkpoint-workstate-conversation-summaries-lot-c](checkpoint-workstate-conversation-summaries-lot-c.md) — memory note checkpoint-workstate-conversation-summaries-lot-c
- [checkpoint-workstate-controlled-actions-lot-d](checkpoint-workstate-controlled-actions-lot-d.md) — memory note checkpoint-workstate-controlled-actions-lot-d
- [checkpoint-work-tab-blank-fix](checkpoint-work-tab-blank-fix.md) — memory note checkpoint-work-tab-blank-fix
- [skills-integration-canonical-foundation](skills-integration-canonical-foundation.md) — Topologie d'intégration du chantier skills agent et décision d'abandon de feature/agent-skills.
- [idea-program-surface-separation-and-livestate](idea-program-surface-separation-and-livestate.md) — Cadrage du programme post-skills : ce qui existe vs reste, et la règle de frontière surface-agent/humaine.
- [handoff-ls5-summary-bound-and-llm-seam](handoff-ls5-summary-bound-and-llm-seam.md) — Contrat figé de la borne de handoff (perf) et du seam résumeur LLM non activé.
- [conversation-log-ls6-rotation-and-paginated-read](conversation-log-ls6-rotation-and-paginated-read.md) — Contrat figé de la rétention/rotation du transcript et de la lecture paginée riche (surface humaine).
- [conversation-viewer-ls7-frontend](conversation-viewer-ls7-frontend.md) — Contrat figé du viewer de transcript paginé (frontend pur, lecture seule, surface humaine).
- [live-state-persistence-program-closed-ls8](live-state-persistence-program-closed-ls8.md) — Le programme live-state + persistance conversationnelle est livré et documenté ; pointe la doc de clôture et les points restés ouverts.
- [resume-after-appimage-rebuild-mcp-e2e](resume-after-appimage-rebuild-mcp-e2e.md) — memory note resume-after-appimage-rebuild-mcp-e2e
- [mcp-e2e-findings-reply-wedge-phantom-busy](mcp-e2e-findings-reply-wedge-phantom-busy.md) — memory note mcp-e2e-findings-reply-wedge-phantom-busy
- [checkpoint-finding-a-fix-built-live-validation-pending](checkpoint-finding-a-fix-built-live-validation-pending.md) — memory note checkpoint-finding-a-fix-built-live-validation-pending
- [git-agent-push-access-gitea-ssh](git-agent-push-access-gitea-ssh.md) — memory note git-agent-push-access-gitea-ssh
- [rendezvous-no-reply-backstop-design](rendezvous-no-reply-backstop-design.md) — Décision d'architecture sur la fin-de-tour et le backstop no-reply du rendez-vous idea_ask_agent ⇄ idea_reply, après échec live du fix Finding A (77e62e5).
- [checkpoint-rendezvous-redesign-devbackend-todo](checkpoint-rendezvous-redesign-devbackend-todo.md) — memory note checkpoint-rendezvous-redesign-devbackend-todo
- [backstop-no-reply-live-test-failed-2026-06-23](backstop-no-reply-live-test-failed-2026-06-23.md) — memory note backstop-no-reply-live-test-failed-2026-06-23
- [live-state-reboot-reconciliation-design](live-state-reboot-reconciliation-design.md) — memory note live-state-reboot-reconciliation-design
- [reconcile-live-state-implemented-feature-branch](reconcile-live-state-implemented-feature-branch.md) — memory note reconcile-live-state-implemented-feature-branch
- [rendezvous-600s-cap-too-short-heavy-tasks](rendezvous-600s-cap-too-short-heavy-tasks.md) — memory note rendezvous-600s-cap-too-short-heavy-tasks
- [backstop-fires-on-intra-task-turn-rootcause](backstop-fires-on-intra-task-turn-rootcause.md) — memory note backstop-fires-on-intra-task-turn-rootcause
- [mcp-functional-test-plan-2026-06-24](mcp-functional-test-plan-2026-06-24.md) — memory note mcp-functional-test-plan-2026-06-24
- [checkpoint-mcp-tests-t1-t5-and-t5-livestate-defect](checkpoint-mcp-tests-t1-t5-and-t5-livestate-defect.md) — memory note checkpoint-mcp-tests-t1-t5-and-t5-livestate-defect
- [checkpoint-mcp-t1-t6-green-t7-fix-wrong-layer](checkpoint-mcp-t1-t6-green-t7-fix-wrong-layer.md) — memory note checkpoint-mcp-t1-t6-green-t7-fix-wrong-layer
- [mcp-t7-backstop-600s-reply-rejected-livestate-stale](mcp-t7-backstop-600s-reply-rejected-livestate-stale.md) — memory note mcp-t7-backstop-600s-reply-rejected-livestate-stale
- [mcp-functional-tests-t1-t9-green-live-2026-06-24](mcp-functional-tests-t1-t9-green-live-2026-06-24.md) — memory note mcp-functional-tests-t1-t9-green-live-2026-06-24
- [mcp-t10a-harness-interrupt-does-not-cancel-rendezvous](mcp-t10a-harness-interrupt-does-not-cancel-rendezvous.md) — memory note mcp-t10a-harness-interrupt-does-not-cancel-rendezvous
- [mcp-t10b-pending-reboot-verification](mcp-t10b-pending-reboot-verification.md) — memory note mcp-t10b-pending-reboot-verification
- [headless-interagent-conversation-objective](headless-interagent-conversation-objective.md) — memory note headless-interagent-conversation-objective
- [background-tasks-first-class-design](background-tasks-first-class-design.md) — memory note background-tasks-first-class-design
- [b7-wiring-anchor-state-rs](b7-wiring-anchor-state-rs.md) — memory note b7-wiring-anchor-state-rs
- [appimage-build-no-strip-relr-dyn-fix](appimage-build-no-strip-relr-dyn-fix.md) — memory note appimage-build-no-strip-relr-dyn-fix
- [issue-ticket-system-design](issue-ticket-system-design.md) — memory note issue-ticket-system-design
- [tickets-frontend-v1-done](tickets-frontend-v1-done.md) — État du frontend du système de tickets — livré, vert, décisions de contrat clés.
- [tickets-v1-e2e-validated-qa](tickets-v1-e2e-validated-qa.md) — Verdict QA du système de tickets V1 sans attachments sur feature/issue-ticket-system — vert de bout en bout, avec la seule réserve du typage de conflit côté UI.
- [b8-command-runner-pty-framing](b8-command-runner-pty-framing.md) — Cadrage hexagonal figé du lot B8 : runner concret sur le port BackgroundTaskRunner, fermeture de la boucle sink en composition root, contrat de complétion, commandes cancel/retry.
- [b8-arbitration-outcomes](b8-arbitration-outcomes.md) — Décisions d'arbitrage Architecture sur les 5 écarts B8 remontés par DevBackend : ce qui reste dans B8 vs part en dette/ticket.
- [b8-in-app-trigger-run-in-background](b8-in-app-trigger-run-in-background.md) — Arbitrage de l'écart de périmètre #1 — la surface agent MCP idea_run_in_background ferme la boucle B8, contrat et lots figés.
- [workstate-background-tasks-projection-fix](workstate-background-tasks-projection-fix.md) — Décision d'archi pour rendre visibles Cancel/Retry dans le panneau Work — extension du read-model GetProjectWorkState, contrat DTO et borne de livraison.
- [tickets-t3-frontend-validation-verdict](tickets-t3-frontend-validation-verdict.md) — Verdict de validation frontend du fix T3 (projection backgroundTasks par agent) sur feature/background-tasks-first-class — vert, avec 3 désalignements de contrat mineurs.
- [ticket4-restart-on-develop-ebd992e](ticket4-restart-on-develop-ebd992e.md) — Cadrage du redémarrage des annonces inter-agent sur la base pré-canonique develop ebd992e — ce qui existe, l'écart live, la réutilisabilité du backup et le découpage B/F.
- [ticket4-overlay-composition-leafview](ticket4-overlay-composition-leafview.md) — Règle de composition des deux voiles plein-cellule de LeafView (write-portal injection PTY vs F3 annonces busy) — exclusion mutuelle avec priorité F3, arbitrée pour éviter le double voile.
- [ticket7-f2-delegated-limit-no-agentratelimited](ticket7-f2-delegated-limit-no-agentratelimited.md) — Écart backend ticket #7/F2 : le rendez-vous délégué n'émet pas AgentRateLimited pour la cible limitée.
- [ui-rework-sprint-scoping-contracts](ui-rework-sprint-scoping-contracts.md) — Frontières hexagonales et contrats figés du sprint UI rework — les 4 tickets sont frontend-purs, la popup TicketPicker #18, l'échelle z-index et le curseur opaque.
- [ticket18-picker-delivered-devfrontend](ticket18-picker-delivered-devfrontend.md) — memory note ticket18-picker-delivered-devfrontend
- [ticket16-menus-floating-delivered-devfrontend](ticket16-menus-floating-delivered-devfrontend.md) — memory note ticket16-menus-floating-delivered-devfrontend
- [floatingwindow-focus-trap-mount-only](floatingwindow-focus-trap-mount-only.md) — Le focus-trap de FloatingWindow ne doit dépendre que du mount ; sinon un onClose instable vole le focus à chaque frappe.
- [ticket17-focus-trap-fix-merged-develop](ticket17-focus-trap-fix-merged-develop.md) — Correctif de la perte de focus dans l'édition de ticket (#17) livré sur develop via feature branch dédiée.
- [ui-rework-sprint-tickets-21-22-23-scoping](ui-rework-sprint-tickets-21-22-23-scoping.md) — memory note ui-rework-sprint-tickets-21-22-23-scoping
- [ui-rework-sprint-delivered-tickets-21-22-23](ui-rework-sprint-delivered-tickets-21-22-23.md) — memory note ui-rework-sprint-delivered-tickets-21-22-23
- [ticket25-assistant-sandbox-eacces-rootcause](ticket25-assistant-sandbox-eacces-rootcause.md) — Root cause et contrat de fix du EACCES (os error 13) au spawn de la CLI d'un assistant de ticket — le preset SandboxPlan::project_read_only handle la classe exec Landlock.
- [ticket14-local-lan-openai-adapter-scoping](ticket14-local-lan-openai-adapter-scoping.md) — memory note ticket14-local-lan-openai-adapter-scoping
- [ticket28-firstrun-detect-hang-scoping](ticket28-firstrun-detect-hang-scoping.md) — Cadrage hexagonal du bug bloquant first-run (#28) — panic block_on imbriqué dans detect + découplage busy/détection, contrats et point de vérité QA.
- [ticket28-f1-frontend-delivered](ticket28-f1-frontend-delivered.md) — Lot F1 du ticket #28 livré sur feature/ticket28-firstrun-detect-hang — invariant busy/detectProfiles, flag detecting, timeout UI, test de non-régression.
- [opencode-llamacpp-replaces-ollama](opencode-llamacpp-replaces-ollama.md) — memory note opencode-llamacpp-replaces-ollama
- [f36-multi-opencode-profiles-frontend](f36-multi-opencode-profiles-frontend.md) — Multiplicité des profils OpenCode locaux côté UI first-run wizard — livrée, verte, sans gap backend.
- [f35-config-crud-delivered](f35-config-crud-delivered.md) — F35.2 (config serveur local + sélection dans les profils OpenCode) livrée et verte sur feature/modeles-locaux ; clôt le sprint modèles locaux F34-F36.
- [develop-realigned-to-cli-ui-baseline-2026-07-02](develop-realigned-to-cli-ui-baseline-2026-07-02.md) — Décision de topologie (validée utilisateur) : develop réaligné sur la ligne UI CLI (pre-chat-ui-baseline).
- [feature-agent-skill-awareness-design](feature-agent-skill-awareness-design.md) — Design validé + découpage de la feature « surfacer les skills assignés à un agent à la manière MCP » (manifeste + idea_skill_read).
- [inter-agent-announcements-feature-and-codex-final-bug](inter-agent-announcements-feature-and-codex-final-bug.md) — Design figé des annonces inter-agent (UI live) + cause racine du bug Final Codex, validé utilisateur le 2026-07-02.
- [inter-agent-live-context-shared-per-agent](inter-agent-live-context-shared-per-agent.md) — Décision d'architecture figée : le contexte live inter-agent est PAR AGENT (partagé), pas par paire (validée utilisateur 2026-07-02).
- [ticket4-announcements-frontend-f1f2f3](ticket4-announcements-frontend-f1f2f3.md) — Topologie du frontend des annonces inter-agent (store borné, preview requester filtré, overlay cible piloté par le busy/idle par agent) — réintégré sur base develop.
- [ticket54-f1-model-download-overlay-frontend](ticket54-f1-model-download-overlay-frontend.md) — Overlay plein-cellule de préparation du serveur modèle local + progression (barre/%/bytes/source), corrélation factorisée, priorité de voile.
- [ticket56-visibleelsewhere-derives-from-layout](ticket56-visibleelsewhere-derives-from-layout.md) — Fix frontend du sélecteur d'agent : la désactivation « visible ailleurs » doit venir du layout courant, pas du dernier nodeId du live registry.
- [ticket13-f0-frontend-transport-inventory](ticket13-f0-frontend-transport-inventory.md) — Résultat de l'inventaire B0/F0 frontend du chantier client/serveur #13 — la frontière gateways transport-neutres existe déjà, F1 = ajout d'adapters HTTP+WS.
- [ticket13-f1-http-ws-adapter-delivered](ticket13-f1-http-ws-adapter-delivered.md) — Le 3e jeu d'adapters frontend (HTTP request/response + squelette WebSocket) du chantier client/serveur #13 est livré et vert, derrière les ports inchangés.
- [ticket13-f2-web-readonly-client-delivered](ticket13-f2-web-readonly-client-delivered.md) — Le client web read-only (pairing cookie, liste projets, ouverture read-only + snapshot work-state) du chantier client/serveur #13 est livré et vert.
- [ticket13-f3-xterm-websocket-delivered](ticket13-f3-xterm-websocket-delivered.md) — L'adapter WS terminal (round-trip xterm, reconnexion+replay, états connexion) du chantier client/serveur #13 est finalisé et vert côté frontend.
- [ticket13-f4-web-agent-surface-delivered](ticket13-f4-web-agent-surface-delivered.md) — L'agent CLI web (agent.launch → terminal.attached, réattache sans relance, cellule agent réutilisant TerminalView) du chantier client/serveur #13 est livré et vert côté frontend.
- [ticket13-f5-web-live-surfaces-delivered](ticket13-f5-web-live-surfaces-delivered.md) — Les surfaces live web (workstate live via event.domain, background tasks cancel/retry, inbox, re-synchro au reconnect) du chantier client/serveur #13 sont livrées et vertes.
- [frontend-uses-npm-not-pnpm](frontend-uses-npm-not-pnpm.md) — memory note frontend-uses-npm-not-pnpm
- [idea-distribution-strategy-desktop-vs-docker](idea-distribution-strategy-desktop-vs-docker.md) — memory note idea-distribution-strategy-desktop-vs-docker
- [web-client-is-single-column-no-desktop-shell](web-client-is-single-column-no-desktop-shell.md) — memory note web-client-is-single-column-no-desktop-shell
- [sandbox-eperm-bind-false-green-web-server](sandbox-eperm-bind-false-green-web-server.md) — memory note sandbox-eperm-bind-false-green-web-server
- [ticket74-f1-web-bundle-transport-seam](ticket74-f1-web-bundle-transport-seam.md) — Deux artefacts Vite (dist/dist-web), mode `web` portable Windows, et le câblage anti-divergence de la constante de transport.

View File

@ -0,0 +1,52 @@
---
name: appimage-build-no-strip-relr-dyn-fix
description: memory note appimage-build-no-strip-relr-dyn-fix
metadata:
type: project
---
---
name: appimage-build-no-strip-relr-dyn-fix
description: Le build AppImage échoue sur cette machine (CachyOS/Arch) à l'étape linuxdeploy/strip (.relr.dyn) ; correctif = NO_STRIP=true. Explique probablement les "builds qui ne produisent rien".
metadata:
type: reference
---
# Build AppImage — échec linuxdeploy `.relr.dyn`, correctif NO_STRIP=true
## Symptôme
`tauri build --bundles appimage` : la **compilation release réussit** (binaire à
`target/release/app-tauri`), puis le **bundling échoue** :
```
[gtk/stdout] ERROR: Strip call failed: .../usr/bin/strip: .../libgobject-2.0.so...:
unknown type [0x13] section `.relr.dyn'
ERROR: Failed to run plugin: gtk (exit code: 1)
failed to bundle project `failed to run .../.cache/tauri/linuxdeploy-x86_64.AppImage`
```
**aucune AppImage produite**, exit code 1. Le `tail` du log ne montrait au début que
« failed to run linuxdeploy » sans détail ⇒ facile à prendre pour un blocage/bash de fond.
## Cause racine (environnement, PAS le code IdeA)
Le `strip` (binutils) embarqué dans le vieux `linuxdeploy-x86_64.AppImage` ne comprend pas la
section ELF moderne `.relr.dyn` (relocations `DT_RELR`, `unknown type [0x13]`) présente dans les
libs système GTK/glibc de CachyOS/Arch (rolling). Le plugin `gtk` de linuxdeploy strip ces libs
→ échec → tout le bundling casse.
## Correctif VALIDÉ (2026-07-02)
Lancer le build avec `NO_STRIP=true` (le binaire release est déjà strippé par cargo) :
```
cd crates/app-tauri
NO_STRIP=true ../../frontend/node_modules/.bin/tauri build --bundles appimage
```
Résultat : `Finished 1 bundle at target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage`.
## Séquence de build complète (rappel, pas de beforeBuildCommand dans tauri.conf.json)
1. `npm --prefix frontend run build` (produit `frontend/dist`).
2. depuis `crates/app-tauri` : `NO_STRIP=true <cli tauri> build --bundles appimage`.
CLI tauri = `frontend/node_modules/.bin/tauri` (@tauri-apps/cli). Config = crates/app-tauri/tauri.conf.json.
## Implication produit
C'est très probablement la vraie cause des « builds AppImage qui ne produisent rien / attente
sans résultat » signalés par l'utilisateur. À intégrer idéalement dans le flux de build d'IdeA
(passer NO_STRIP quand on cible AppImage sur Arch, ou vendoriser un linuxdeploy récent).
Lien : [[mcp-bridge-and-delegation-runtime-notes]],
[[checkpoint-b2-bootstrap-applied-await-codex-reset-1430]].

View File

@ -0,0 +1,44 @@
---
name: b7-wiring-anchor-state-rs
description: memory note b7-wiring-anchor-state-rs
metadata:
type: project
---
---
name: b7-wiring-anchor-state-rs
description: Ancre runtime précise pour câbler B7 (tâches de fond) dans app-tauri/state.rs — repérée par Main pour dé-risquer la délégation DevBackend.
metadata:
type: reference
---
# B7 — Ancre de câblage runtime (crates/app-tauri/src/state.rs)
Composition root = builder `AppState` dans `crates/app-tauri/src/state.rs`. Repères exacts
(lecture Main 2026-07-02, base `feature/background-tasks-first-class`) :
- **Mailbox / inbox** construits l.1207-1242 : `InMemoryMailbox::new()` (1207) → `mailbox`
(1208, `dyn AgentMailbox`) → `MediatedInbox::with_pty(...)` (1213) → `input_mediator`
(1242, `dyn InputMediator`). C'est LA file existante ; B4 (`AgentInbox`) s'y branche.
- **clock** dispo : `Arc<dyn Clock>` (cloné partout, ex. 1255, 1384).
- **Pattern de boucle de fond OBLIGATOIRE** : `tauri::async_runtime::spawn` (PAS `tokio::spawn`
`build` tourne dans le hook `setup` sans runtime tokio ambiant). Exemples : sweep_stalled
l.1234, drain scheduler session-limit l.1317. Le sink B3 + bridge B4 + wake B5 doivent être
spawnés sur ce même pattern.
- **Builder OrchestratorService** l.1356-1454 : chaîne `.with_input_mediator` (1369),
`.with_events`, `.with_record_turn`, `.with_live_state`/`.with_live_state_read` (providers
PAR ROOT, root fixée par appel), `.with_ask_liveness_probe`, `.with_ask_ceiling`,
`.with_structured` (1453).
**⚠ POINT CLÉ : `.with_background_tasks(store, clock)` de B6 N'EST PAS présent ici** → le
rendez-vous-as-task est DORMANT au runtime. B7 doit l'ajouter.
- **Nuance per-root** : `FsBackgroundTaskStore` écrit `<root>/.ideai/background-tasks/<projectId>.json`
(per-projet), mais `OrchestratorService` est GLOBAL multi-projets. Suivre le précédent
`AppLiveStateProvider` / `AppReconcileLiveState` (provider keyé par root, câblé dans
`open_project`, cf. commentaire l.1247-1252) plutôt qu'une instance de store globale unique.
- **Reconcile au boot** : précédent = `reconcile_live_state` (l.1252) appelé dans `open_project`
(commands.rs l.134). Le reconcile des tâches de fond + ré-enqueue des complétions non livrées
peut se greffer au même endroit (per project root à l'ouverture).
Point d'accroche B8 (déféré, couplage PTY) : création commande longue côté `pty.rs` /
`LocalProcessSpawner` → créer `BackgroundTask{kind:Command}` au spawn + pousser exit/stdout
dans le sink.
Lien : [[background-tasks-first-class-design]], [[checkpoint-b2-bootstrap-applied-await-codex-reset-1430]].

View File

@ -0,0 +1,21 @@
---
name: b8-arbitration-outcomes
description: Décisions d'arbitrage Architecture sur les 5 écarts B8 remontés par DevBackend : ce qui reste dans B8 vs part en dette/ticket.
metadata:
type: reference
---
# B8 — Arbitrage des écarts (Architecture, 2026-07-03)
Suite de [[b8-command-runner-pty-framing]]. Build vert (sink 6/6, tail 5/5, b7 1/1).
1. **Tee PTY live** : ACCEPTER B8 sans rendu live (bloqueur levé). Le cœur complétion→wake fonctionne ; tee = orthogonal. NE PAS toucher PtyPort ni broadcast multi-conso dans B8. Dette = ticket A : ajouter `PtyPort::wait`/`try_wait` pour découpler détection d'exit de la consommation d'output (l'EOF-comme-proxy-de-fin est fragile), puis tee UI. Pas de broadcast multi-consommateur.
2. **BackgroundCommandArchive** : REFACTORER — retirer le trait DOMAINE. Retry = registre runtime in-memory (app/runner), NON persisté (SpawnSpec porte des secrets, ne pas écrire dans .ideai/background-tasks/*.json qui voyage avec le projet). Conséquence : retry SESSION-SCOPED en V1, pas après reboot. Reboot-retry = dette ticket B (persistance sûre = redaction/store machine-local hors projet).
3. **spawn_background_command** : ACCEPTER, nécessaire (point d'entrée manquant du cadrage). B8 a donc 4 commandes : spawn/cancel/retry/list.
4. **stderr_tail = None** : ACCEPTER V1. Sémantique pty correcte (TTY fusionne stdout/stderr). Champ réservé à une future variante ProcessSpawner-backed. stdout_tail = sortie tty fusionnée.
5. **list sans agentId dégradé** : ACCEPTER V1 (F2 centré agent). Limite : store n'énumère pas les tâches terminales/ouvertes par projet ; historique completed/failed partiel. Enrichir BackgroundTaskStore = dette ticket B.
## Tickets de suivi à ouvrir
- A : PtyPort::wait + tee live + robustesse détection de fin.
- B : persistance sûre de l'invocation (retry-after-reboot, secrets) + énumération terminale/projet du store.
B8 clôturable une fois le point 2 refactoré + points 1/5 tracés en dette.

View File

@ -0,0 +1,28 @@
---
name: b8-command-runner-pty-framing
description: Cadrage hexagonal figé du lot B8 : runner concret sur le port BackgroundTaskRunner, fermeture de la boucle sink en composition root, contrat de complétion, commandes cancel/retry.
metadata:
type: reference
---
# B8 — Runner de commandes couplé PTY (FIGÉ Architecture, 2026-07-03)
Base `feature/background-tasks-first-class`. Fait suite à [[background-tasks-first-class-design]] et [[b7-wiring-anchor-state-rs]].
## Trou constaté
`BackgroundCompletionSink::new`/`start_from_runner` n'existent QUE dans les tests. En prod `state.rs`, `background_ready_tx` n'est alimenté que par le reconcile boot ; aucun `impl BackgroundTaskRunner` concret. `LocalProcessSpawner.run` = one-shot Output, non couplé.
## Décisions
- **Le spawner ne crée pas la task.** Concepts owner/project/wake_policy = application. On implémente le port FIGÉ `BackgroundTaskRunner` (domain/ports.rs:1349) via nouvel adapter infra `CommandBackgroundRunner` composant `Arc<dyn PtyPort>` (résolu via RemoteHost → Liskov SSH/WSL), PAS un PortablePtyAdapter en dur.
- Application = use case `SpawnBackgroundCommand` : alloue TaskId, store.create(Queued)→Running, runner.spawn(BackgroundTaskSpec). Le spec (ports.rs:207) porte déjà task_id/project_id/owner_agent_id/kind/wake_policy/command:Option<SpawnSpec>/deadline.
- Le runner n'écrit NI store NI inbox : il émet 1 `BackgroundTaskCompletion` sur subscribe_completions(). Le sink (single-writer) fait store.save PUIS ready_tx.send (persist-avant-signal).
- Composition root ferme la boucle : construire runner + `BackgroundCompletionSink::new(store_port, background_ready_tx)` + `sink.start_from_runner(runner)` sur `tauri::async_runtime::spawn` (pas de tokio ambiant). Ancre state.rs ~1540-1660.
- Frontière : seuls SpawnSpec/BackgroundTaskSpec/BackgroundTaskCompletion (domaine) franchissent ; portable-pty reste en infra. Rendu xterm = tee du même spawn vers PtyBridge, orthogonal au tracking.
## Contrat complétion
BackgroundTaskResult Success/Failure { finished_at_ms, exit_code:Some, summary, stdout_tail, stderr_tail bornés (ring UTF-8-safe, cap IDEA_BG_TAIL_BYTES ~8-16KiB) }. Cancel→runner kill→Cancelled{reason}, pas de wake succès. Invariants tenus par le sink (dédup task_id, IgnoredAlreadyTerminal, mark_completion_delivered) + pont enqueue_message (jamais busy) + wake si idle. Final reste terminal-de-tour, la complétion est un InboxItem::BackgroundCompletion.
## Commandes Tauri (dépend B8)
cancel_background_task({taskId}); retry_background_task({taskId})→BackgroundTaskDto (NOUVEAU task_id, jamais réutilisé); list_background_tasks({projectId,agentId?}). DTO camelCase {taskId,ownerAgentId,projectId,kind,state,exitCode?,summary?,stdoutTail?,stderrTail?,createdAtMs,updatedAtMs}.
## Sous-tâches DevBackend (ordre)
1 util tail borné (infra). 2 CommandBackgroundRunner (infra/background_task.rs + lib.rs). 3 use cases SpawnBackgroundCommand/Cancel/Retry (application). 4 fermer boucle sink en composition root (app-tauri/state.rs). 5 handlers+DTO (commands.rs, events.rs). 6 tee PTY live (pty.rs). 7 tests (réutiliser tests/background_completion_sink.rs).

View File

@ -0,0 +1,30 @@
---
name: b8-in-app-trigger-run-in-background
description: Arbitrage de l'écart de périmètre #1 — la surface agent MCP idea_run_in_background ferme la boucle B8, contrat et lots figés.
metadata:
type: reference
---
# B8 — Déclencheur in-app (FIGÉ Architecture, 2026-07-03)
Suite de [[b8-command-runner-pty-framing]] et [[b8-arbitration-outcomes]]. Constat : le consommateur B8 (runner PTY → sink → inbox → wake) est livré (8cac147) mais SANS producteur — `spawn_background_command` (commands.rs:2586) n'est appelé par personne. #1 n'a donc aucun déclencheur réel.
## Décision
- **Retenu : outil MCP agent `idea_run_in_background`** (surface principale, fermante). Chemin humain UI (bouton panel F2) = secondaire optionnel, `wake_policy=RecordOnly`. **Promotion auto d'une commande PTY longue : REJETÉE** (pas d'owner/intention, heuristique fragile, change la sémantique de write_terminal).
- Pourquoi (a) : toute la machinerie (WakeOwner, inbox, AgentWakePort) est agent-centrée ; seul un agent déclarant explicitement une tâche de fond exerce la boucle bout-en-bout.
## Contrat `idea_run_in_background`
Params : label(req), command(req), args[], cwd?(déf=project root), deadline_ms?. **owner = identité handshake du demandeur (non paramètre, non usurpable, rejet si absente)** ; project = contexte du demandeur ; record_only NON exposé côté agent → **WakeOwner forcé**. Retour SYNCHRONE {taskId,state} ; le résultat arrive plus tard en InboxItem::BackgroundCompletion (fire-and-forget-avec-tracking, distinct d'idea_ask_agent synchrone).
## Frontière — ZÉRO nouveau port/adapter
- Domaine : variante `OrchestratorCommand::RunInBackground` (domain/orchestrator.rs).
- Adapter MCP : décl outil + map_tool_call (mcp/tools.rs), comme idea_ask_agent.
- Exécution : handler route vers use case EXISTANT application::SpawnBackgroundCommand (déjà en composition root state.rs), owner=requester, WakeOwner. Suivre le MÊME dispatch qu'AskAgent pour atteindre la couche app ; ne pas ré-router par commande Tauri front.
## Lots
- BE-1 : variante enum + outil + mapping + tests mapping.
- BE-2 : handler RunInBackground → SpawnBackgroundCommand (rejet si demandeur absent).
- FE-1 (secondaire) : formulaire création dans panel F2 → spawn_background_command record_only.
- QA T1 : agent appelle idea_run_in_background(`sh -c 'sleep 8; echo done'`), finit son tour → owner ré-invoqué avec exit+résumé. T3 cancel/retry sur ce taskId (retry session-scoped, pas reboot — dette ticket B).
## Branche
Tient sur feature/background-tasks-first-class (additif, réutilise la boucle figée). Rien à préparer côté Git.

View File

@ -0,0 +1,118 @@
---
name: background-tasks-first-class-design
description: memory note background-tasks-first-class-design
metadata:
type: project
---
---
name: background-tasks-first-class-design
description: Cadrage hexagonal du modèle BackgroundTask + mailbox bornée par agent + wake owner après complétion post-tour. Fait autorité pour les lots B1-B7 / F1-F4.
metadata:
type: reference
---
# Tâches de fond de 1re classe — CADRAGE FIGÉ (Architect, 2026-07-02)
Base : `feature/background-tasks-first-class` (@ fccc1e2, empilée sur v2 `62915ee`+`fccc1e2`,
au-dessus de develop `a9653bc`, ligne CLI/PTY « toujours headless »).
## Objectif (2 défauts à corriger)
1. **Complétion post-tour perdue** : une tâche de fond (ex. build `run_in_background`) finit
après la fin du tour de l'agent → IdeA ne ré-invoque pas le propriétaire avec le résultat.
2. **Pas de mailbox** : message concurrent pendant working/waiting rejeté « still busy » au
lieu d'être mis en file et drainé au tour suivant.
## Modèle
`BackgroundTask` { task_id, owner_agent_id, project_id, kind, state, started/updated_at_ms,
deadline_ms?, correlation, result, wake_policy }.
- kind : `Command | HeadlessRendezvous | SessionResume | Maintenance`
- state : `Queued | Running | Waiting | Completed | Failed | Cancelled | Expired`
- result : `None | Success(payload) | Failure(error) | Cancelled(reason)`
- wake_policy : `WakeOwner | RecordOnly`
Règle centrale : la complétion n'est JAMAIS seulement un retour de future local ; elle est
persistée/observée comme événement de tâche, puis transformée en message mailbox pour le
propriétaire.
## Réutilisé vs nouveau
Réutilisé : `InputMediator`/`AgentMailbox` (FIFO par agent), `TicketId`, `AgentBusyState`,
`run_ask_with_watchdog`+plafond+liveness, `live-state.json` (projection maigre),
`ReplyEvent::Final`/`Announcement`, session-limit scheduler (réveil différé annulable).
Nouveau : `BackgroundTask` persistant, store/registry, completion sink durable,
`AgentWakePort`, mailbox entrante bornée par agent (couvre user/agents/complétions système),
reconcile au boot.
## Ports
- `BackgroundTaskStore` : create/get/save/list_open_for_agent/list_undelivered_completions/
mark_completion_delivered. **B1 figé** (async_trait, `BackgroundTaskPortError`).
- `BackgroundTaskRunner` : spawn(spec)->handle / cancel / subscribe_completions()->stream.
**B1 figé.**
- `AgentInbox` (façade au-dessus d'`InputMediator`) : enqueue_message / dequeue_next /
snapshot. **Lot B4.**
- `AgentWakePort` : wake_agent(project, agent, reason) — ne connaît PAS Tauri ; adapter
lance/rattache session structured/headless. **Lot B5.**
## Contrats/DTO
`InboxItem` { id, agent_id, source: Human|Agent{id}|BackgroundTask{task_id}|System,
kind: UserMessage|AgentDelegation|BackgroundCompletion|ResumeNotice, body, created_at_ms,
correlation_id?, priority (FIFO défaut, pas de priorité cachée V1) }.
`BackgroundCompletionDto` camelCase { taskId, ownerAgentId, projectId, kind, status,
exitCode, stdoutTail, stderrTail, finishedAtMs }.
Events `DomainEvent` : BackgroundTaskStarted/Progress(borné)/Completed/Failed/Cancelled,
AgentInboxQueued/Drained, AgentWakeScheduled/Started/Failed. (Events = observabilité/UI ;
store+mailbox = autorité.)
## Flux nominal run_in_background
1 agent lance commande → 2 crée BackgroundTask{owner,Running} → 3 adapter démarre → 4 tour
peut finir → 5 commande finit plus tard → 6 completion sink reçoit exit/stdout/stderr →
7 écrit Completed/Failed dans store → 8 enqueue InboxItem::BackgroundCompletion → 9 si owner
Idle: AgentWakePort.wake_agent démarre nouveau tour headless avec résultat → 10 si Busy/Waiting:
FIFO, drainé au prochain tour libre. **Idempotence : 1 seule completion livrée par task_id ;
crash entre store et wake réparé par reconcile.**
## Mailbox bornée
1 par agent, FIFO stricte, capacité configurable (`IDEA_AGENT_INBOX_CAPACITY`, défaut 100).
Enqueue pendant Working/Waiting → `queued` (plus jamais `busy`). Overflow : messages
humains/agents → `InboxFull` typée ; complétions de tâches → persistées delivery_pending,
JAMAIS perdues. « Busy » = tour en cours, pas entrée refusée.
## Lots backend
- **B1** domaine : types, ports Store/Runner, events, invariants purs. ✅ FAIT (12 tests).
(AgentInbox/AgentWakePort reportés à B4/B5.)
- **B2** store+registry : adapter FS/sqlite-like simple, écriture atomique, reconcile boot
(Running sans handle vivant → Unknown/Failed ou delivery_pending).
- **B3** completion sink : runner publie complétions sur canal interne ; sink persiste AVANT
tout wake ; tests idempotence double completion.
- **B4** mailbox unifiée : étendre `InputMediator`/`AgentMailbox` (pas de 2e FIFO concurrente) ;
enqueue_message, snapshots workstate ; remplacer refus « still busy » par `queued`.
- **B5** wake owner : adapter `AgentWakePort` au-dessus de `StructuredSessions`/
`AgentSession::send` ; wake seulement si idle ; sinon launch/rattach headless ; résultat
injecté comme message système explicite.
- **B6** rendezvous-as-task : `idea_ask_agent` reste synchrone pour l'appelant mais son
exécution interne = BackgroundTask{HeadlessRendezvous} ; timeouts/backstops → résultats de
tâche, plus des états invisibles.
- **B7** reconcile boot : lire store, ré-enqueue complétions non livrées, recalculer live-state.
## Lots frontend
- **F1** workstate : queue depth par agent, « Queued » au lieu de « busy rejected ».
- **F2** panel tâches de fond : liste par agent running/completed/failed ; cancel/open output/retry.
- **F3** agent cell : badge « messages en attente » + « tâche de fond terminée » ; pas de
transcript brut auto-injecté.
- **F4** notifications : toast sobre à la fin d'une tâche longue ; clic ouvre owner/détail.
## Invariants
completion persistée avant wake ; livrée au plus une fois ; aucun message vers agent connu
rejeté pour Busy ; mailbox ne dépasse jamais capacité ; overflow ne perd jamais une completion
système ; 1 item traité à la fois ; live-state = projection jamais autorité ; `Final` reste le
seul terminal normal headless ; un redémarrage n'oublie pas les complétions persistées non
drainées.
## Critères QA (backend)
commande background finissant après le tour → owner réveillé avec exit code + résumé ; idem
avec IdeA redémarré entre fin et wake → completion retrouvée ; message user à agent busy →
`queued` ; deux agents vers même agent busy → FIFO ; mailbox pleine → `InboxFull` sur message
normal, completion système en pending ; double event même task_id → 1 livraison ; annulation →
`Cancelled`, pas de wake succès ; rendezvous silencieux → backstop = tâche Failed/NoReply,
libère la queue ; session-limit resume continue et ne contourne pas la mailbox.
Liens : [[checkpoint-b2-bootstrap-applied-await-codex-reset-1430]],
[[rendezvous-no-reply-backstop-design]], [[session-limit-handling-design]],
[[headless-interagent-conversation-objective]], [[inter-agent-live-context-shared-per-agent]].

View File

@ -1,34 +0,0 @@
---
name: backstop-fires-on-intra-task-turn-rootcause
description: memory note backstop-fires-on-intra-task-turn-rootcause
metadata:
type: project
---
---
title: CAUSE RACINE — le backstop tire sur chaque tour interne (turn_duration ≠ retour-au-prompt)
type: project
description: Le turn-watcher fait tirer le backstop no-reply au PREMIER turn_duration, soit le 1er pas d'agent interne — il casse toute délégation multi-étapes (libération prématurée + rapport perdu). Le test QA pong passait par chance (tâche mono-tour).
---
# CAUSE RACINE (2026-06-23, prouvée live) — backstop tire sur les tours INTERNES
## Preuve
Cadrage délégué à Architect (tâche multi-étapes, lecture de code) :
```
562100 [rendezvous] ask started -> Architect turn_timeout_ms=600000
564943 [rendezvous] ask returned-to-prompt-no-reply: Architect after_ms=2843
564943 [mailbox] complete_without_reply agent=dce19c75 ticket=cc4067fa outcome=sent
```
- Tiré à **2843 ms** : impossible qu'Architect ait fini un cadrage en 2,8 s.
- Au même instant, transcript Architect **toujours en écriture** (Bash/Read en boucle), `idea_reply` count = **0** → Architect **continuait à travailler**.
- Transcript = **61 `turn_duration`** : Claude écrit un `turn_duration` **par tour d'agent interne** (chaque appel LLM de la boucle agentique), PAS seulement au retour-au-prompt final.
## Diagnostic
`claude_turn_watcher.rs` fait tirer `turn_ended` au **premier incrément** de `turn_duration` au-dessus de la baseline. Or cet incrément survient dès le **1er pas interne** de l'agent (~secondes). Donc le backstop confond « l'agent a fait un pas de raisonnement » avec « l'agent est retourné au prompt sans répondre ». Pour **toute tâche multi-étapes** : libération prématurée du demandeur « sans réponse » pendant que la cible bosse encore → `idea_reply` final → « no pending request » → **rapport perdu** (travail survit sur disque/branche).
Le test QA « pong » (backstop-no-reply-live-test-failed-2026-06-23, devenu vert) passait **par chance** : tâche à **un seul tour** ⇒ premier tour = dernier tour. Le fix `encode_cwd` a fait tirer le watcher correctement, mais a **révélé que la CONDITION de tir est fausse**.
## Conséquence opérationnelle
Avec le binaire courant, **aucune délégation substantielle (multi-tours) ne remonte son rapport** : elle est coupée à ~3 s. Workaround Main en attendant le fix : déléguer → surveiller le transcript de la cible jusqu'à inactivité (mtime stable) → récupérer le résultat sur disque/branche ou re-solliciter en léger.
## Implication pour la refonte (seam liveness/fin-de-tour, validée utilisateur)
« Retourné au prompt idle » NE peut PAS être « un turn_duration est apparu ». Le bon signal = **process vivant (`is_agent_live`) + transcript NE grossit PLUS depuis stall_after_ms** (battement de cœur). Tant que le transcript grossit ⇒ l'agent travaille ⇒ ne pas libérer. Le 1er lot du fix devrait **neutraliser ce tir prématuré**. Lié à [[rendezvous-600s-cap-too-short-heavy-tasks]], [[rendezvous-no-reply-backstop-design]], [[backstop-no-reply-live-test-failed-2026-06-23]].

View File

@ -1,34 +0,0 @@
---
name: backstop-no-reply-live-test-failed-2026-06-23
description: memory note backstop-no-reply-live-test-failed-2026-06-23
metadata:
type: project
---
---
title: Backstop no-reply — RE-VALIDATION LIVE VERTE (wedge levé) après fix encode_cwd
type: project
description: Le backstop no-reply du rendez-vous est validé en live le 2026-06-23 (session post-rebuild) : demandeur libéré ~grâce 2s au lieu de 600s. Reste un point de finition (résultat véhiculé via canal d'erreur).
---
# Backstop no-reply — RE-VALIDATION LIVE : VERTE (2026-06-23, session post-rebuild)
Remplace le 1er test (ÉCHEC) du même jour. La cause racine était l'encodage du slug projet Claude (`.` non encodé) corrigé dans `crates/infrastructure/src/inspector/claude_paths.rs` (`encode_cwd``replace(/[^a-zA-Z0-9]/g,'-')`, double-tiret sur `/.ideai/`). Voir [[checkpoint-rendezvous-redesign-devbackend-todo]] pour la cause racine.
## Preuves live (idea.log, session a6ced819)
Scénario : DevBackend → idea_ask_agent vers QA « pong en prose, SANS idea_reply (volontaire) ».
```
912948 [mcp] ask armed requester=DevBackend(73c853d1) target=QA timeout_ms=600000
917707 [turn-watcher] turn_duration detected agent=QA(aefdbd61) count 47->48 -> turn_ended
917707 [input-mediator] turn_ended agent=QA no_reply_payload -> mark_idle + grace
917707 [input-mediator] turn_ended arming grace agent=QA ticket=0257c630 grace_ms=2000
919707 [mailbox] complete_without_reply agent=QA ticket=0257c630 queue_depth_before=1 after=0 outcome=sent
```
Contraste avec l'échec précédent : `turn_ended` = 0 avant le fix, ici il FIRE ; `arm … dir=…IdeA--ideai-run-…` sur dossier EXISTANT ; grâce 2s exacte (917707→919707) ; libération du demandeur via `complete_without_reply outcome=sent`. DevBackend a repris la main en ~13 s mesuré (pas 600 s). Aucun wedge, aucun cascade-timeout.
## Point de finition résiduel (NON bloquant, follow-up)
Le résultat rendu au demandeur passe par le **canal d'erreur** MCP : « agent QA returned to its prompt without calling idea_reply (no answer was rendered); retry the request and ensure the agent replies via idea_reply ». Ce n'est pas un résultat no-reply synthétique structuré (design item c de [[rendezvous-no-reply-backstop-design]]) ; pour un orchestrateur c'est mal distinguable d'un échec transitoire « retry ». À transformer en verdict consommable (follow-up, hors blocage merge).
## Suite
Wedge prouvé levé en live ⇒ garde-fou Git satisfait. Délégué à Git : commit du fix `encode_cwd` (+ test de non-régression `encode_cwd_encodes_dot_in_run_dir_to_double_dash`) sur la branche du backstop, puis décision de merge develop.

View File

@ -1,62 +0,0 @@
---
name: checkpoint-auto-memory-harvest-lot-e1
description: memory note checkpoint-auto-memory-harvest-lot-e1
metadata:
type: project
---
# Checkpoint — Auto-memory harvest Lot E1
Date: 2026-06-21
## État final
Lot E1 `auto-memory harvest contrôlé` terminé, validé QA et mergé localement dans `develop`.
Branche finale:
- `develop` @ `61c330a` — merge local `feature/controlled-auto-memory-harvest`.
- Branche `feature/controlled-auto-memory-harvest` supprimée après merge.
- Aucun push, aucun merge vers `main`.
- Working tree applicatif propre; seuls résidus non committés: bruit runtime `.ideai/*` volontairement exclu.
## Commits créés
- `b12081b``feat(memory): récolte automatique contrôlée de la mémoire (Lot E1)`
- Domaine: parsing pur de blocs fenced `idea-memory` avec limites et erreurs isolées.
- Application: `HarvestMemoryFromTurn` best-effort sur `TurnRole::Response`, persistance via `MemoryStore`, événements `MemorySaved` via `EventBus`.
- Orchestrateur: harvest branché après checkpoint/handoff sur chemins de succès ask/reply, non bloquant.
- Lifecycle: directive mémoire IdeA injectée dans le contexte convention-file.
- App/Tauri: wiring du use case dans `AppState`.
- Frontend: refresh du panneau mémoire sur `memorySaved`; patch test-only mock gateway `workState`.
- `8c7c47c``fix(mcp): fallback déterministe du runtime dir socket`
- Durcit le choix du runtime dir socket MCP si `XDG_RUNTIME_DIR` est inutilisable.
- Commit séparé car indépendant du Lot E1.
- `61c330a``merge(memory): intègre la récolte automatique contrôlée (Lot E1) dans develop`.
## QA verte
Commandes QA réelles:
- `cargo fmt --all -- --check` OK.
- `cargo check -p app-tauri` OK.
- `cd frontend && npx tsc --noEmit` OK.
- `cd frontend && npx vitest run` OK après patch test-only, `42 passed`, `405 tests`.
- `cargo test -p domain memory_harvest -- --nocapture` OK, 14 passed.
- `cargo test -p application --test memory_harvest -- --nocapture` OK, 5 passed.
- `cargo test -p application --test orchestrator_service harvest -- --nocapture` OK, 4 passed.
- `cargo test -p infrastructure --test memory_store idempotent -- --nocapture` OK, 1 passed.
- `cargo test --workspace` brut rouge uniquement sur tests AF_UNIX/MCP bloqués par sandbox `Operation not permitted`; workspace filtré strictement sur ces tests environnementaux OK.
## Décisions produit/techniques
- Harvest strictement best-effort: jamais bloquant pour réponse, délégation, handoff ou ticket.
- Mémoire durable seulement via directive explicite `idea-memory`, plafonnée et validée; pas de log brut, pas de live-state, pas de handoff dans ce mécanisme.
- `title` est exigé par directive mais non persisté car l'entité `Memory` n'a pas de champ titre; accepté pour E1 afin de ne pas élargir le lot.
- Les fichiers runtime `.ideai/*` générés pendant la session restent hors commits applicatifs.
## Suite probable
Prochains chantiers à cadrer par Architect avant implémentation:
- persistance conversationnelle/canonical log + handoff incrémental cross-profile;
- live-state projet persistant et séparé de la mémoire durable;
- synchronisation documentaire architecture après les lots Work B/C/D et E1.

View File

@ -1,46 +0,0 @@
---
name: checkpoint-blocked-until-appimage-030-restart
description: memory note checkpoint-blocked-until-appimage-030-restart
metadata:
type: project
---
# Checkpoint — blocage délégation Architect jusqu'au redémarrage AppImage 0.3.0
Date: 2026-06-20
## État atteint
Chantier `orchestrator-designation` terminé et intégré localement:
- `develop` @ `9c71a5b` après commit runtime post-build.
- Worktree propre à ce moment.
- AppImage corrigée générée:
`/home/anthony/Documents/Projects/IdeA/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage`
- Artefact vérifié:
- taille 106M
- `--appimage-offset` = `944632`
## Problème live confirmé
Deux tentatives de délégation vers Architect ont affiché le texte dans le chat/terminal d'Architect sans le soumettre réellement.
L'utilisateur a interrompu et confirmé: « Le message n'arrive pas jusqu'à Architecte visiblement ».
Ce symptôme correspond au bug live de soumission write-portal/headless delivery corrigé dans `orchestrator-designation`, mais l'application en cours tourne encore avec l'ancien AppImage. Le nouvel artefact n'est pas chargé tant qu'IdeA n'est pas relancé avec l'AppImage 0.3.0.
## Action effectuée
Architect stoppé pour éviter un terminal avec tâche fantôme.
## Prochain chantier prévu après redémarrage
Git a recommandé:
- Prochain chantier: `feature/agent-skill-awareness`.
- Architect doit d'abord trancher le chevauchement avec `feature/agent-skills`.
- `fix/cold-start-delivery-race` est probablement obsolète/superseded par `feature/agent-skill-awareness`, mais à supprimer seulement après intégration ou décision Git finale.
## Reprise recommandée
1. Relancer IdeA avec:
`/home/anthony/Documents/Projects/IdeA/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage`
2. Reprendre par une délégation Architect:
cadrage `agent-skill-awareness` vs `agent-skills`.
3. Ensuite cycle normal: Git -> DevBackend -> QA -> Git.

View File

@ -1,88 +0,0 @@
---
name: checkpoint-delivery-submit-logging-fix
description: memory note checkpoint-delivery-submit-logging-fix
metadata:
type: project
---
# Checkpoint — hotfix livraison délégation + logs submit
Date: 2026-06-20
## Pourquoi ce checkpoint existe
Après le fix AppImage 0.3.0 précédent, la délégation `Main -> DevBackend` a reproduit le même symptôme que `Main -> Architect` avant redémarrage : le message de délégation apparaît dans l'input de la cellule cible, mais n'est pas soumis.
Le chantier `feature/agent-skill-awareness-v2` est donc suspendu tant que le canal de délégation n'est pas fiable.
## Branche et état
Branche active pendant le hotfix : `feature/agent-skill-awareness-v2`.
Dirty attendu hors code : fichiers runtime `.ideai/conversations/*`, `.ideai/layouts.json` produits par la session live.
## Changements code effectués
Frontend :
- `frontend/src/features/terminals/useWritePortal.ts`
- Logs détaillés `[write-portal]` : abonnement, attachement front, réception `delegationReady`, chunks de texte, délai avant submit, submit, ack, erreurs.
- Fix défensif : `submitSequence: ""` est normalisée vers `"\r"` au lieu d'écrire zéro octet.
- En cas d'erreur d'injection, arrêt du retry immédiat infini.
- `frontend/src/adapters/terminal.ts`
- Logs `[terminal-write]` sur writes non triviaux ou contrôles (`\r`, `\n`, bulk, etc.).
- `frontend/src/adapters/input.ts`
- Logs `[input-gateway]` pour `delegationDelivered` et `setFrontAttached`.
- `frontend/src/domain/index.ts`
- Commentaire défaut submit aligné sur ~350 ms.
- `frontend/src/features/terminals/useWritePortal.test.tsx`
- Test ajouté : `submitSequence: ""` doit envoyer `"\r"`.
Backend/application/Tauri :
- `crates/application/src/orchestrator/service.rs`
- `note_delegation_delivered` passe par `application::diag!` persistant.
- `set_agent_front_attached` logge les changements d'attachement et le cas médiateur absent.
- `crates/infrastructure/src/input/mod.rs`
- Logs détaillés `[input-mediator]` : start turn, gate cold start, publish `DelegationReady`, choix front-owned vs headless, bind handle, front attach/detach, headless text/submit start/ok/failure.
- Défaut headless `DEFAULT_SUBMIT_DELAY_MS` corrigé de 60 ms à 350 ms pour être aligné avec le write-portal.
- Headless normalise aussi une submit sequence vide vers `"\r"`.
- `crates/app-tauri/src/commands.rs`
- Logs persistants `[pty-write]` autour de `write_terminal` avec session, bytes, contrôle sans contenu bulk.
- Logs persistants `[delivery]` pour `delegation_delivered` et `set_front_attached`.
## Vérifications passées
- `cargo fmt --all -- --check` : OK.
- `npx vitest run src/features/terminals/useWritePortal.test.tsx src/adapters/terminal.test.ts src/features/terminals/TerminalView.portal.test.tsx` : OK, 3 files / 20 tests.
- `npx tsc --noEmit` : OK.
- `cargo test -p infrastructure input --lib` : OK, 35 tests.
- `cargo check -p app-tauri` : OK.
- `cargo test -p application --test orchestrator_service` : OK, 45 tests (warning existant `CapturingFs::writes` unused).
- `cargo test -p app-tauri --test orchestrator_wiring` : OK, 13 tests.
## Build AppImage
Commande Tauri standard : frontend build OK, Rust release OK, bundling Tauri KO avec `failed to run linuxdeploy` comme précédemment.
Contournement réussi :
```bash
ARCH=x86_64 APPIMAGE_EXTRACT_AND_RUN=1 NO_STRIP=1 \
/tmp/appimage_extracted_02f96dbd6ecc47f616b5a57fb8b7aa60/usr/bin/appimagetool \
--runtime-file /tmp/idea-appimage-runtime-x86_64 \
/home/anthony/Documents/Projects/IdeA/target/release/bundle/appimage/IdeA.AppDir \
/home/anthony/Documents/Projects/IdeA/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage
```
Artefact :
- `/home/anthony/Documents/Projects/IdeA/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage`
- taille : 106M
- `--appimage-offset` : `944632`
## Reprise recommandée
1. Relancer IdeA avec l'AppImage ci-dessus.
2. Reproduire une délégation simple `Main -> DevBackend` ou `Main -> Architect`.
3. Si le message reste dans l'input sans être soumis, récupérer les traces :
- logs persistants `application::diag!` (chemin configuré au startup, typiquement app-data logs/idea.log), chercher `[delivery]`, `[pty-write]`, `[rendezvous]`.
- console frontend, chercher `[write-portal]`, `[terminal-write]`, `[input-gateway]`.
- stderr si lancé depuis terminal, chercher `[input-mediator]`.
4. Une fois délégation fiable, reprendre `feature/agent-skill-awareness-v2` depuis le cadrage Architect déjà obtenu.

View File

@ -1,37 +0,0 @@
---
name: checkpoint-finding-a-fix-built-live-validation-pending
description: memory note checkpoint-finding-a-fix-built-live-validation-pending
metadata:
type: project
---
# Checkpoint — Finding A : fix confirmé EN CODE + AppImage clean DÉPLOYÉE → reste la validation e2e LIVE (MAJ 2026-06-23 09:33)
Suite de [[mcp-e2e-findings-reply-wedge-phantom-busy]] (le défaut) et [[resume-after-appimage-rebuild-mcp-e2e]] (méthode).
## État du code (OK, vert)
- Fix Finding A **mergé dans `develop @ 77e62e5`** (par-dessus feat `c5e5493`).
- 3 lots : **Lot 1** préambule `idea_reply` impératif (`infrastructure/src/input/mod.rs` + `application/src/agent/lifecycle.rs`) ; **Lot 2 = LE fix** `with_prompt_ready_pattern("? for shortcuts")` sur le profil Claude (`application/src/agent/catalogue.rs:80`) + overlay `overlay_reference_defaults` ; **Lot 3** backstop timeout typé `TARGET_RETURNED_NO_REPLY` / env `IDEA_ASK_RENDEZVOUS_TIMEOUT_MS` (`infrastructure/src/orchestrator/mcp/server.rs`).
- Tests lib qui PROUVENT le seed (re-passés vert le 2026-06-23) : `application::agent::catalogue::mcp_tests::claude_seed_carries_the_measured_prompt_ready_pattern` ✓ et `codex_seed_leaves_prompt_ready_pattern_empty_for_now` ✓.
## LEÇON D'OUTILLAGE (important, éviter de refaire l'erreur)
**Ne PAS utiliser `strings`/`grep` sur le binaire comme oracle de présence du fix.** Le compilateur élimine/inline les littéraux uniques de `catalogue.rs` : `? for shortcuts` ET `codex --version` sont ABSENTS du binaire alors que le code et les tests les portent (les littéraux aussi présents ailleurs — `claude --version`, `.mcp.json`, `CLAUDE.md` — n'apparaissent que par co-référence). Oracle fiable = **tests** + **comportement live**, pas le grep binaire. (Une fausse alarme « build incrémental incohérent » a été levée puis invalidée ainsi.)
## Mise en service FAITE
- Rebuild clean (`cargo clean -p application -p infrastructure` puis recette tauri) → artefact `…/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage`.
- **Déployé** sur `/home/anthony/Documents/IdeA_0.3.0_amd64.AppImage` (rename atomique ; SHA256 `138714473ed6a87b…` installée == artefact). Ancienne sauvegardée `…AppImage.bak-23pre`.
- ⚠️ **L'instance IdeA qui tourne encore est l'ANCIENNE** (FUSE déjà monté). Il faut que **l'utilisateur RELANCE IdeA** pour charger le nouveau binaire — cela redémarre tous les agents, dont Main (la session courante meurt).
## PROCHAINE ÉTAPE après redémarrage : test e2e LIVE
Méthode (rappel) : **NE PAS valider le système inter-agents VIA `idea_ask_agent`**. Piloter via subagents natifs / l'utilisateur. Diag EXTERNE : `ss -xp | grep idea-mcp` (ponts) ; transcripts `~/.claude/projects/<run-dir-encodé>/*.jsonl` (horodatages UTC ; reply réel = `tool_use` idea_reply ; wedge = transcript demandeur figé sur `tool_use` idea_ask_agent sans `tool_result`).
Scénario qui wedgeait (doit maintenant passer) :
1. Faire déléguer **DevBackend → QA** : « réponds juste pong ».
2. ATTENDU avec le fix : si QA répond en prose sans `idea_reply`, dans la grâce `PROMPT_READY_GRACE` le retour au prompt idle (`? for shortcuts`) déclenche `complete_without_reply`**DevBackend reçoit `TargetReturnedNoReply` (retryable) et REPREND la main** (plus de wedge) ; `idea_workstate` QA repasse `working``idle`/`done` (plus de Busy fantôme). Lot 1 devrait en plus pousser QA à appeler `idea_reply` (chemin positif → DevBackend reçoit pong).
3. Non-régression : chemin positif normal (QA appelle `idea_reply`) → demandeur reçoit le résultat, workstate→done ; livraison 1er tour à froid intacte.
4. Calibrer `PROMPT_READY_GRACE` si la fenêtre est trop courte/longue en réel (défaut conservé).
## Après validation verte
Merge `develop` déjà fait. Reste éventuelle release (0.4.0 ?) = décision utilisateur. NB : **Git est de nouveau strictement local, PLUS d'accès push** (la note `git-agent-push-access-gitea-ssh` a été corrigée en ce sens). `develop` est ~69 commits devant `origin/develop` (sync remote hors périmètre Git).
## Follow-ups différés (hors PR)
Capture `prompt_ready_pattern` Codex ; généralisation merge profils de référence ; e2e délégation background (node_id None) / profils MCP défaut / observabilité UI des délégations ; durcissement doc `idea_reply` dans `CLAUDE.md` versionné (router à Git).

View File

@ -1,40 +0,0 @@
---
name: checkpoint-mcp-t1-t6-green-t7-fix-wrong-layer
description: memory note checkpoint-mcp-t1-t6-green-t7-fix-wrong-layer
metadata:
type: project
---
---
name: checkpoint-mcp-t1-t6-green-t7-fix-wrong-layer
description: T1→T6 verts (non-régression). T7 ROUGE diagnostiqué (fix sur mauvaise couche) PUIS RE-FIX implémenté dans le working tree + vert en tests unitaires. Reste : rebuild AppImage en cours → relance IdeA → re-valider T1→T7 en live → commit Git.
metadata:
type: project
---
# Checkpoint tests fonctionnels MCP — run a6ced819 (2026-06-24)
Plan via skill **mcp-rendezvous-functional-test**, Main = demandeur. Setup chauds : Main/QA/DevFrontend ; froids : Architect/DevBackend/Git.
## T1→T6 : VERTS sur AppImage 09:21 (aucune régression)
T1 visibilité, T2 chaude `pong`, T3 réveil froid Git (1er tour intact), T4 background node_id None, T5 no-reply backstop (live-state purgée), T6 multi-étapes == oracle. Détails dans l'historique. NB outillage : `pgrep -af mcp-server | grep <uuid>` = FAUX positifs (le shell de mesure se matche) → filtrer `ps -eo cmd | grep 'app-tauri mcp-server'`.
## T7 : défaut trouvé EN LIVE puis CORRIGÉ
**Défaut (T7e live, 621 s ≈ 600 s)** : cible en long tour unique > 600 s (transcript en croissance prouvée) coupée par un timeout PLAT `process error: agent session reply timed out`, pas d'extension. Le watchdog du fix précédent vivait côté requester (`server.rs`) mais le seam qui coupe vraiment est `application/src/orchestrator/service.rs` (`ASK_AGENT_TIMEOUT`=600s plat appliqué au drain de tour délégué, ~L1564). **Fix posé sur la mauvaise couche.**
**RE-FIX implémenté (subagent natif Architect→DevBackend→QA, worktree, ramené dans le main working tree ; HEAD toujours `1efe2f1`, NON committé)** :
- Découverte clé : `ClaudeSdkSession::send().await` rend TOUS les `ReplyEvent` en bloc À LA FIN du tour (rien mid-turn) → la vivacité par-événement est inutilisable ; seul signe de vie réel = **sonde transcript** (octets `.jsonl`), comme `server.rs`.
- Un seul `run_inactivity_watchdog` générique dans **`crates/application/src/orchestrator/rendezvous.rs`** (NOUVEAU, pur, testé), réutilisé par `service.rs` (le vrai seam) ET `server.rs` (copie privée supprimée → dé-duplication).
- Nouveau port `AskLivenessProbe` injecté au composition root `app-tauri/state.rs``transcript_activity_token` (+ `with_ask_ceiling`, env `IDEA_ASK_RENDEZVOUS_CEILING_MS`, défaut 4 h). Sans injection ⇒ fallback fenêtre plate = zéro régression.
- Issues typées : progrès<plafond réarme ; progrès+plafond `AppError::TargetCeilingActive` (code `RENDEZVOUS_CEILING_ACTIVE`, non-retry-aveugle) ; vrai silence `AgentSessionError::Timeout` inchangé.
- Défaut secondaire (busy fantôme) : `mark_target_done_best_effort` sur toutes les branches d'abandon (PTY + structuré).
- Fichiers : application `{rendezvous.rs(new), service.rs, error.rs, lib.rs, orchestrator/mod.rs}`, infra `mcp/server.rs`, `app-tauri/state.rs` (+ les modifs infra inspector/lib pré-existantes du 1er fix).
- **Preuve réelle (re-vérifiée par Main dans le MAIN TREE)** : `cargo test -p application --lib` 75/0 (dont progressing_target_extends_then_resolves, _hits_ceiling_distinctly, silent_target_expires_no_reply, no_probe_falls_back_to_flat_window, ceiling_active_code) ; `cargo test -p infrastructure --lib` 247/0 ; clippy 0 nouveau warning.
## PROCHAINE ÉTAPE (reprise après relance IdeA)
1. **Rebuild AppImage EN COURS** (recette : `npm --prefix frontend run build` puis depuis `crates/app-tauri/` `APPIMAGE_EXTRACT_AND_RUN=1 NO_STRIP=1 ../../frontend/node_modules/.bin/tauri build --bundles appimage` ; artefact `target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage` remplace `/home/anthony/Documents/IdeA_0.3.0_amd64.AppImage`, backup l'ancienne). Log build : scratchpad/rebuild.log.
2. **Utilisateur relance IdeA** (tue Main).
3. **Reprendre le test depuis T1** (skill). Le but immédiat = **re-valider T7 EN LIVE** : déléguer une charge réelle > 600 s en un long tour (la machine est rapide : `cargo clean && cargo build --release --workspace` en boucle jusqu'à >13 min, transcript en croissance) ⇒ ATTENDU = PAS de timeout à 600 s, extension, reply final livré. Puis enchaîner T8 (parallèles), T9 (transitif A→B→C, Architect+DevBackend froids), T10 (interruption + reconcile reboot).
4. **Si live vert : demander à Git de committer** le fix (cf. [[git-owns-commit-merge-decisions]]) ; rendez-vous MCP court marche (T2-T6), donc commit délégable à Git OU subagent natif.
Lié à [[rendezvous-600s-cap-too-short-heavy-tasks]], [[mcp-functional-test-plan-2026-06-24]], [[mcp-bridge-and-delegation-runtime-notes]].
</content>
</invoke>

View File

@ -1,29 +0,0 @@
---
name: checkpoint-mcp-tests-t1-t5-and-t5-livestate-defect
description: memory note checkpoint-mcp-tests-t1-t5-and-t5-livestate-defect
metadata:
type: project
---
---
name: checkpoint-mcp-tests-t1-t5-and-t5-livestate-defect
description: Run a6ced819 — relance complète T1→T6 du test fonctionnel MCP sur AppImage 08:41 (fix T5). T5 (live-state purgée sur no-reply) VALIDÉ EN LIVE. Blocage à T7 = défaut plafond 600s connu, non corrigé.
metadata:
type: project
---
# Checkpoint tests fonctionnels MCP — run a6ced819 (2026-06-24, relance depuis T1)
Binaire validé : **AppImage 2026-06-24 08:41:30** (fix live-state no-reply), process en cours = cette AppImage, git HEAD `1efe2f1`. Plan via skill **mcp-rendezvous-functional-test** (Main = demandeur). Voir [[mcp-functional-test-plan-2026-06-24]], [[rendezvous-600s-cap-too-short-heavy-tasks]], [[backstop-fires-on-intra-task-turn-rootcause]].
## Résultats de la relance T1→T6 (tous ✅)
- **T1** visibilité outils ✅ — 3 requesters chauds (Main, QA, DevFrontend) chacun pont ESTAB + 2 mcp-server ; froids (Architect, DevBackend, Git) 0 process.
- **T2** cible chaude (QA) ✅ — `pong` inline borné, workstate `done`, pas de busy fantôme.
- **T3** réveil à froid (Git) ✅ — nouveau pont (26012/26016), 1er tour `[IdeA·tâche…]` `parentUuid:null` intact, `delivered`, `pong-cold`.
- **T4** background node_id None (Git hors layouts.json) ✅ — `bg-step-ok`, multi-`delivered`, pont stable sans dup.
- **T5** no-reply backstop ✅ **VALIDÉ EN LIVE (le fix marche)** — Main libéré borné avec `TargetReturnedNoReply` (« returned to its prompt without calling idea_reply », retryable) ET **QA `status:"done"` purgée** (plus de busy fantôme `working`). Défaut historique #1 fermé.
- **T6** multi-étapes (QA lit fichier + `find` puis reply) ✅ — pas de libération prématurée, reply réel `header=… ; rs_count=55` (concorde avec `find` indépendant). Piège turn-watcher non déclenché.
## Blocage actuel : T7 (NON lancé — défaut connu non corrigé)
T7 = tâche lourde > plafond 600s. C'est le finding [[rendezvous-600s-cap-too-short-heavy-tasks]] : dépassement du plafond `IDEA_ASK_RENDEZVOUS_TIMEOUT_MS` (défaut 600000) → faux `-32001` + canal de report mort (idea_reply final non livrable), alors que la cible travaille et finit sur la branche. Les pistes (extension sur signe de vie / message « cible active, plafond atteint » distinct du retryable / plafond configurable / pas de cascade) sont **à cadrer Architect, pas encore implémentées**. Lancer T7 en live = figer Main ~10 min pour re-confirmer un défaut déjà documenté.
## Prochaine étape (décision utilisateur en attente)
Trois options sur T7 : (a) le lancer quand même pour re-confirmer (wedge ~10 min) ; (b) ouvrir le cycle de fix du plafond maintenant (Architect→Git→DevBackend→QA, rebuild, reprise T1) ; (c) sauter T7 et continuer T8 (parallèles), T9 (transitif A→B→C), T10 (interruption + reconcile reboot) qui ne dépendent pas du plafond. Architect/DevBackend restent froids pour T9.

View File

@ -1,98 +0,0 @@
---
name: checkpoint-orchestrator-designation-appimage-build
description: memory note checkpoint-orchestrator-designation-appimage-build
metadata:
type: project
---
# Checkpoint — AppImage rebuild après orchestrator-designation
Date: 2026-06-20
## État Git avant build
Git avait clôturé `feature/orchestrator-designation`:
- Commits atomiques créés:
- `287681c feat(orchestrator): ...`
- `e462136 feat(terminals): ...`
- `09f5362 docs: ...`
- `5ef001e chore(wip): ...`
- Merge local dans `develop`: `55d887f merge(orchestrator): intègre le chantier orchestrator-designation dans develop`.
- Branche `feature/orchestrator-designation` supprimée.
- Worktree propre à ce moment-là.
## Rebuild tenté
Commande initiale:
```bash
npm --prefix frontend run build && \
cd crates/app-tauri && \
APPIMAGE_EXTRACT_AND_RUN=1 NO_STRIP=1 ../../frontend/node_modules/.bin/tauri build --bundles appimage
```
Résultat:
- Frontend build OK.
- Rust release build OK: `/home/anthony/Documents/Projects/IdeA/target/release/app-tauri`.
- AppDir généré: `/home/anthony/Documents/Projects/IdeA/target/release/bundle/appimage/IdeA.AppDir`.
- Bundling Tauri KO: `failed to run linuxdeploy`.
## Contournement réussi
`appimagetool` extrait existait dans:
```text
/tmp/appimage_extracted_02f96dbd6ecc47f616b5a57fb8b7aa60/usr/bin/appimagetool
```
L'appel direct à appimagetool échouait car réseau restreint:
```text
Failed to download runtime ... pass it to appimagetool with --runtime-file
```
Runtime extrait depuis l'ancienne AppImage locale:
```bash
/home/anthony/Documents/IdeA_0.2.0_amd64.AppImage --appimage-offset
# 944632
dd if=/home/anthony/Documents/IdeA_0.2.0_amd64.AppImage \
of=/tmp/idea-appimage-runtime-x86_64 \
bs=1 count=944632 status=none
chmod +x /tmp/idea-appimage-runtime-x86_64
```
Commande finale réussie:
```bash
ARCH=x86_64 APPIMAGE_EXTRACT_AND_RUN=1 NO_STRIP=1 \
/tmp/appimage_extracted_02f96dbd6ecc47f616b5a57fb8b7aa60/usr/bin/appimagetool \
--runtime-file /tmp/idea-appimage-runtime-x86_64 \
/home/anthony/Documents/Projects/IdeA/target/release/bundle/appimage/IdeA.AppDir \
/home/anthony/Documents/Projects/IdeA/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage
```
Artefact produit:
```text
/home/anthony/Documents/Projects/IdeA/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage
Taille: 106M
--appimage-offset: 944632
```
## État Git après build
Le build/la session a laissé du runtime dirty:
```text
## develop...origin/develop [ahead 8]
M .ideai/conversations/6bc594e8-a37c-0dbd-1de6-6e3b73002cb4/handoff.md
M .ideai/conversations/6bc594e8-a37c-0dbd-1de6-6e3b73002cb4/log.jsonl
M .ideai/layouts.json
```
Ne pas nettoyer arbitrairement. Demander à Git de décider si ces fichiers runtime doivent être commités/ignorés au prochain passage.
## Important live
L'AppImage en cours d'exécution ne charge pas automatiquement ce nouvel artefact. Pour utiliser les correctifs d'orchestration/write-portal, lancer l'artefact 0.3.0 produit ci-dessus ou remplacer l'AppImage installée hors sandbox si nécessaire.

View File

@ -1,54 +0,0 @@
---
name: checkpoint-orchestrator-designation-backend-compile-fix
description: memory note checkpoint-orchestrator-designation-backend-compile-fix
metadata:
type: project
---
# Checkpoint — orchestrator-designation backend compile fix
Date: 2026-06-20
## Branche
`feature/orchestrator-designation`.
## Décision Git
Finir le chantier courant sur cette branche. Pas de stash, pas de switch. Après tests verts ou résidu qualifié: retour Git pour commits atomiques puis merge local vers `develop`.
## Incident live
La délégation vers Architect a été affichée dans son chat mais non soumise. Architect a été stoppé. Symptôme cohérent avec l'AppImage live qui n'intègre pas encore les correctifs WIP write-portal/headless delivery.
## Vérifications Main avant correction DevBackend
Verts:
- `cargo test -p infrastructure input --lib`: 35 passed.
- `cargo test -p application --test orchestrator_service`: 45 passed.
- `cd frontend && npx vitest run`: 41 files, 384 tests passed.
- `cd frontend && npx tsc --noEmit`: OK.
Rouge initial:
- `cargo test --workspace` ne compilait pas `crates/application/src/orchestrator/context_guard.rs`:
- `may_write_directly` appelé avec 2 args au lieu de 3 (`OrchestratorDesignation` manquant).
- mauvais champ `orchestrator` mis sur `ManifestEntry` au lieu de `AgentManifest`.
- init `AgentManifest` sans `orchestrator`.
## Correction DevBackend
DevBackend a modifié `crates/application/src/orchestrator/context_guard.rs`:
- `ProposeContext` charge `AgentManifest`, récupère `manifest.orchestrator_designation()`, puis appelle `may_write_directly(requester, &GuardedResource::ProjectContext, &designation)`.
- Le `FileGuard` reste un verrou, pas le propriétaire de l'autorisation.
- Tests locaux adaptés à `AgentManifest { version, entries, orchestrator }`.
Validations DevBackend:
- `cargo fmt --all && cargo test -p application --test orchestrator_service`: OK, 45 passed.
- `cargo test -p application`: OK.
- `cargo test --workspace`: compile maintenant plus loin mais échoue dans `app-tauri` sur tests loopback Unix socket sous sandbox (`PermissionDenied`/`Operation not permitted` lors du bind `/run/user/1000/idea-mcp/*.sock`).
## Prochaine étape
QA doit qualifier le résidu app-tauri:
- confirmer que les suites métier/orchestration sont vertes,
- confirmer si les échecs app-tauri sont environnement/sandbox et non régression,
- proposer la commande de validation acceptable ou le besoin de correction test/env.

View File

@ -1,42 +0,0 @@
---
name: checkpoint-orchestrator-designation-qa-verdict
description: memory note checkpoint-orchestrator-designation-qa-verdict
metadata:
type: project
---
# Checkpoint — QA verdict orchestrator-designation
Date: 2026-06-20
## Branche
`feature/orchestrator-designation`.
## Verdict QA
Chantier validable sous contrainte d'environnement. Pas de régression fonctionnelle liée à `context_guard.rs`.
## Commandes vertes qui font foi
- `cargo fmt --all -- --check`: OK.
- `cargo test -p application --test orchestrator_service`: OK, 45 passed.
- `cargo test -p application`: OK, suite application complète verte, incluant les tests context guard orchestrateur/proposition.
- `cargo test -p infrastructure input --lib`: OK, 35 passed.
- `cd frontend && npx vitest run`: OK, 41 files / 384 tests passed.
- `cd frontend && npx tsc --noEmit`: OK.
## Résidu app-tauri
- `cargo test -p app-tauri --lib`: rouge, 39 passed / 8 failed.
- `cargo test --workspace`: rouge sur le même bloc app-tauri.
Échecs exacts: tests loopback Unix socket:
- `mcp_bridge::tests::end_to_end_over_real_loopback`
- `state::bind_endpoint_d1_tests::rebind_after_corpse_socket_succeeds`
- `state::mcp_e2e_loopback_tests::*`
Cause qualifiée par QA: contrainte environnement/sandbox. Une sonde Node minimale échoue aussi à `listen()` sur Unix socket avec `EPERM` sous `/run/user/1000` et sous `/tmp`. Donc les tests qui exigent un vrai socket Unix ne sont pas exécutables dans ce sandbox.
## Prochaine étape
Retour à Git pour commits atomiques sur `feature/orchestrator-designation`, puis décision de merge local vers `develop` selon sa stratégie. Aucun push.

View File

@ -1,38 +0,0 @@
---
name: checkpoint-orchestrator-designation-restart
description: memory note checkpoint-orchestrator-designation-restart
metadata:
type: project
---
# Checkpoint — reprise chantier orchestrator-designation
Date: 2026-06-20
## Situation
L'utilisateur a demandé de terminer les chantiers ouverts. Main a repris le cycle proprement.
## Décision Git obtenue
Git a inspecté l'état local et décidé:
- Branche courante: `feature/orchestrator-designation`.
- Worktree dirty important mais analysé comme mono-thème: chantier orchestrateur/designation + diagnostic inter-agents.
- Ne pas stash, ne pas switch, ne pas ouvrir une nouvelle branche maintenant.
- Premier chantier à fermer: `orchestrator-designation` sur la branche actuelle.
- Après stabilisation + tests verts: retour à Git pour commits atomiques puis merge local vers `develop`.
- Ensuite seulement ouvrir des branches dédiées pour les autres chantiers.
## Incident de délégation
Main a tenté de demander à Architect de cadrer `orchestrator-designation` via `idea_ask_agent`.
L'utilisateur a interrompu et signalé qu'Architect était bloqué avec le texte de la tâche affiché dans son chat mais non envoyé.
Interprétation: symptôme cohérent avec les problèmes actuels de write portal / soumission physique PTY / délégation visible-background. Ne pas empiler de nouvelles délégations tant que cette zone n'est pas stabilisée.
## Prochaine reprise recommandée
1. Nettoyer/arrêter l'agent Architect bloqué si nécessaire.
2. Reprendre le chantier courant localement en lecture seule pour identifier exactement les changements WIP liés à la soumission de délégations.
3. Comme Main ne code pas, utiliser la délégation seulement vers un agent dont la soumission est confirmée fonctionnelle, ou lancer les agents en visible et vérifier qu'ils reçoivent réellement la tâche.
4. Priorité technique immédiate: fermer `orchestrator-designation`, car la délégation inter-agents fiable conditionne tous les autres chantiers.

View File

@ -1,38 +0,0 @@
---
name: checkpoint-rendezvous-redesign-devbackend-todo
description: memory note checkpoint-rendezvous-redesign-devbackend-todo
metadata:
type: project
---
# Checkpoint — Backstop no-reply : 1ère validation live ÉCHOUÉE → cause racine corrigée → RE-VALIDATION LIVE EN ATTENTE (MAJ 2026-06-23 ~19:40)
## Résultat du 1er test live (AppImage 0.3.0 du commit e87c05f) : ÉCHEC reproduisant le wedge
Scénario joué : DevBackend→QA `ping (test backstop)`, QA répond `pong` EN PROSE sans `idea_reply`.
- QA a bien émis un vrai `turn_duration durationMs=2218` (transcript, ts 17:23:40.161Z).
- MAIS `[turn-watcher] turn_ended` JAMAIS émis (`grep -c turn_ended` sur tout le log = **0**) → DevBackend resté coincé → rendez-vous Main→DevBackend a timeout 600s (MCP error -32001).
## CAUSE RACINE (prouvée) — encodage du slug projet Claude faux
Traces `[turn-watcher] arm` : toutes `conversation=<none>` + `baseline … count=0`. Le dossier transcript calculé n'existait pas :
- calculé (faux) : `…/-home-…-IdeA-.ideai-run-<uuid>` (point conservé) → `ls` = absent
- réel (disque) : `…/-home-…-IdeA--ideai-run-<uuid>` (DOUBLE tiret)
`encode_cwd` (crates/infrastructure/src/inspector/claude_paths.rs) ne remplaçait que `/`/`\\` et gardait le `.`. Claude Code encode `replace(/[^a-zA-Z0-9]/g,'-')`. Dir introuvable → conversation non résolue → baseline 0 → `turn_duration` jamais vu → backstop mort.
Log de référence : `/home/anthony/.local/share/app.idea.ide/logs/idea.log`.
## FIX LIVRÉ par DevBackend (NON commité — Git tranche)
- `encode_cwd` : `.map(|c| if c.is_ascii_alphanumeric() { c } else { '-' })` (algo exact Claude Code). `/IdeA/.ideai/run``-IdeA--ideai-run`.
- Test non-régression ajouté `encode_cwd_encodes_dot_in_run_dir_to_double_dash` (PASS). Test Windows maj `C:\\Users\\me``C--Users-me`.
- DevBackend précise : `count_turn_ends` agrège déjà tous les `.jsonl` du dossier ⇒ pas besoin de choisir un .jsonl ; `conversation=<none>` à l'armement est normal et sans impact une fois le dir correct.
## QA PRÉ-VALIDATION : VERTE
`cargo test --workspace` = **1616 passed, 0 failed** ; `cargo clippy --workspace --all-targets` = 0 erreur. Nouveau test + 5 tests turn-watcher tous PASS.
## EN COURS : rebuild AppImage (par Main, perms project-root OK)
Recette : `npm --prefix frontend run build` puis depuis `crates/app-tauri/` `APPIMAGE_EXTRACT_AND_RUN=1 NO_STRIP=1 ../../frontend/node_modules/.bin/tauri build --bundles appimage`. Logs scratchpad fe-build.log / appimage-build.log. Sortie : `/home/anthony/Documents/Projects/IdeA/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage`.
## SEUL RESTE À FAIRE = RE-VALIDATION LIVE (garde-fou Git : pas de merge sans wedge prouvé levé EN LIVE)
1. Utilisateur remplace l'AppImage installée par la nouvelle + relance IdeA (coupe la session).
2. Session fraîche : rejouer DevBackend→QA pong-sans-`idea_reply`. ATTENDU cette fois : trace `arm … dir=…IdeA--ideai-run-… conversation=…` sur dossier EXISTANT + baseline NON nulle + `[turn-watcher] turn_duration detected … -> turn_ended` ⇒ libération du demandeur ~grâce (≈2s) avec résultat no-reply synthétique, et non-régression du chemin propre `idea_reply`.
3. Si VERT → demander à Git d'amender/committer le fix sur `feature/rendezvous-no-reply-backstop` puis décider du merge develop. Si KO → relayer trace réelle à DevBackend.
## Trou produit confirmé (hors scope, à planifier)
Au reboot, `.ideai/live-state.json` non réconcilié : agents restent `working` fantôme (process morts). Fichier file-guardé, `idea_workstate_set` = self only, aucun reset cross-agent. Manque : réconciliation au boot OU outil orchestrateur de reset.

View File

@ -1,34 +0,0 @@
---
name: checkpoint-work-tab-blank-fix
description: memory note checkpoint-work-tab-blank-fix
metadata:
type: project
---
# Checkpoint — Fix onglet Work vide
Date: 2026-06-21
## État final
Correctif frontend de l'onglet Work vide terminé, validé QA (VERT) et mergé localement dans `develop`.
- `develop` @ `a66881d` — merge local `--no-ff` du fix.
- Commit de feature intégré : `3e1e553` (anciennement `9d2f186`, message amendé pour retirer le marqueur `(WIP, QA en attente)`).
- Branche `fix/work-tab-blank-page` supprimée après merge.
- Aucun push, aucune PR. `develop` en avance de 45 commits sur `origin/develop` (local-only).
## Contenu du fix
Normalisation défensive du work-state (`frontend/src/adapters/workStateNormalization.ts` nouveau) + adaptation panneau/mock/tests, pour qu'un work-state partiel/inattendu ne rende plus une page entièrement vide. 5 fichiers, +259/-47.
## QA verte (sortie réelle)
- `cd frontend && npx tsc --noEmit` → exit 0.
- `cd frontend && npx vitest run` → 42 fichiers / 407 tests passed, exit 0.
## Branches ouvertes restantes (non mergées dans develop)
À cadrer/arbitrer avant intégration :
- `feature/agent-skills` @ `ef101db` — domaine skills agent + use cases + FS store + injection LaunchAgent (L12).
- `feature/agent-skill-awareness` @ `5be8987` — manifeste skills + outil MCP `idea_skill_read` + brief « capacités IdeA » dans contexte agent ; contient aussi un fix input cold-start (`e93a2c1`).
- `fix/cold-start-delivery-race` @ `9590eac` — fix livraison délégation cold-start ; branche basée sur d'anciens commits release (0.1.0/0.2.0), potentiellement redondante avec `e93a2c1` de skill-awareness → vérifier avant merge.

View File

@ -1,64 +0,0 @@
---
name: checkpoint-workstate-controlled-actions-lot-d
description: memory note checkpoint-workstate-controlled-actions-lot-d
metadata:
type: project
---
# Checkpoint — Workstate controlled actions Lot D
Date: 2026-06-20
## État final
Lot D `workstate controlled actions` terminé, validé QA et mergé localement dans `develop`.
Branche finale:
- `develop` @ `c1e99d1` — merge local `feature/workstate-controlled-actions`.
- `feature/workstate-controlled-actions` conservée.
- Aucun push, aucune branche supprimée.
- Working tree propre après décision Git.
## Commits créés
- `3408c96``feat(workstate): actions contrôlées sur le work-state (Lot D backend)`
- Use cases agent-level `AttachLiveAgent` et `StopLiveAgent`.
- Commandes Tauri `attach_live_agent` et `stop_live_agent`.
- Attach rebind PTY/structured sans spawn; stop PTY/structured.
- DTO camelCase et wiring AppState/lib.
- `1c6441b``feat(workstate): UI des actions contrôlées (Lot D frontend)`
- Port/adaptateur agent alignés sur le nouveau contrat attach + stop.
- Panneau Work: Open, Attach, Stop, View conversation, Copy summary.
- Work n'appelle pas `launchAgent`; Stop passe par `stopLiveAgent`; View/Copy n'utilisent que les previews Lot C.
- `0976648``chore(wip): état runtime .ideai (flux conversation live)`
- Runtime isolé des commits feature.
- `c1e99d1` — merge local dans `develop`.
## QA verte
Commandes QA/Git validées:
- `cargo fmt --all -- --check` OK.
- `cargo test -p application --test workstate_actions` OK, 7 passed.
- `cargo test -p application --test workstate` OK, 21 passed.
- `cargo test -p app-tauri --test dto_agents` OK, 25 passed.
- `cargo test -p app-tauri --test list_live_agents_r0b` OK, 5 passed.
- `cargo check -p app-tauri` OK.
- `cd frontend && npx vitest run src/features/workstate/workstate.test.tsx src/features/projects/projects.test.tsx` OK, 25 tests.
- `cd frontend && npx vitest run src/adapters/agent.test.ts` OK, 8 tests.
- `cd frontend && npx vitest run src/features/layout/singletonAgent.test.tsx src/features/layout/agentAlreadyRunning.test.tsx` OK, 9 tests.
- `cd frontend && npx tsc --noEmit` OK.
Warnings restants uniquement côté Vite sur options `esbuild` dépréciées/ignorées au profit de `oxc`, non bloquants.
## Décisions produit/techniques
- Actions Lot D opèrent seulement sur l'état existant; pas de lancement d'agent neuf depuis Work.
- Attach ne crée ni session ni cellule; cible déterministe = cellule visible vide côté UI.
- Stop ne supprime ni agent, ni tickets, ni handoff/conversation summary.
- View/Copy restent limités aux previews bornées Lot C; aucun log brut exposé.
## Suite probable
La quadrilogie Work A/B/C/D est intégrée. Prochains chantiers restants à cadrer: persistance conversationnelle/cross-profile plus profonde, mise à jour automatique mémoire/contexte pendant la vie des agents, ou synchronisation documentaire architecture selon priorité Architect/Main.

View File

@ -1,60 +0,0 @@
---
name: checkpoint-workstate-conversation-summaries-lot-c
description: memory note checkpoint-workstate-conversation-summaries-lot-c
metadata:
type: project
---
# Checkpoint — Workstate conversation summaries Lot C
Date: 2026-06-20
## État final
Lot C `workstate conversation summaries` terminé, validé QA et mergé localement dans `develop`.
Branche finale:
- `develop` @ `6e1ba7e` — merge local `feature/workstate-conversation-summaries`.
- `feature/workstate-conversation-summaries` conservée.
- Aucun push, aucune branche supprimée.
- Working tree propre après décision Git.
## Commits créés
- `e9edadc``feat(workstate): résumés de conversation dans le read-model (Lot C backend)`
- `ProjectWorkState.conversations` top-level.
- Résumés best-effort depuis `HandoffStore`, fallback `ConversationLog::last(3)`.
- Déduplication des conversation ids issus des tickets, ordre first-seen.
- DTO camelCase et câblage Tauri.
- `c50622e``feat(workstate): UI des résumés de conversation (Lot C frontend)`
- Types TS `ConversationWorkSummary` et previews.
- Mock normalise `conversations: []`.
- Panneau Work joint `tickets[].conversationId` vers `conversations[]` et affiche badge + preview compacte.
- `78500d8``chore(wip): état runtime .ideai (flux conversation live)`
- Runtime isolé des commits feature.
- `6e1ba7e` — merge local dans `develop`.
## QA verte
Commandes QA finales validées:
- `cargo fmt --all -- --check` OK.
- `cargo test -p application --test workstate` OK, 21 passed, aucun warning Rust.
- `cargo test -p app-tauri --test dto_agents` OK, 21 passed.
- `cargo check -p app-tauri` OK.
- `cd frontend && npx vitest run src/features/workstate/workstate.test.tsx src/features/projects/projects.test.tsx` OK, 2 files / 19 tests.
- `cd frontend && npx tsc --noEmit` OK.
Warnings restants uniquement côté Vite sur options `esbuild` dépréciées/ignorées au profit de `oxc`, non bloquants.
## Décisions produit/techniques
- Source primaire: handoff conversationnel existant.
- Fallback: derniers tours bornés du log, jamais le log brut complet.
- Read-only et best-effort: les erreurs de preview ne bloquent pas `live/busy/tickets`.
- Pas de nouvelle persistance, pas de mutation/réparation, pas de nouvelle action UX.
## Suite probable
Le prochain lot logique est Lot D: actions UX read/write contrôlées autour du panneau Work (ouvrir/rattacher cellule, voir conversation, arrêter agent, éventuellement copier résumé), à cadrer par Architect avant implémentation.

View File

@ -1,62 +0,0 @@
---
name: checkpoint-workstate-delegation-queue-lot-b
description: memory note checkpoint-workstate-delegation-queue-lot-b
metadata:
type: project
---
# Checkpoint — Workstate delegation/queue snapshot Lot B
Date: 2026-06-20
## État final
Lot B `workstate delegation/queue snapshot` terminé, validé QA et mergé localement dans `develop`.
Branche finale:
- `develop` @ `64c2c14` — merge local `feature/workstate-delegation-queue`.
- `feature/workstate-delegation-queue` conservée.
- Aucun push, aucune branche supprimée.
- Working tree propre après décision Git.
## Commits créés
- `cc7d99a``feat(workstate): snapshot des délégations en file par agent (Lot B backend)`
- `QueuedTicketSnapshot` + port read-only `AgentQueueSnapshot`.
- Impl `InMemoryMailbox` snapshot FIFO sans cloner les senders.
- `GetProjectWorkState.agents[].tickets` avec statut dérivé `inProgress/queued`, source `human/agent`, preview bornée.
- DTO Tauri camelCase et wiring `AppState` avec le même mailbox partagé en deux ports.
- `c600604``feat(workstate): UI des délégations en file par agent (Lot B frontend)`
- Types TS `AgentTicketState`, `TicketWorkStatus`, `TicketWorkSource`.
- Mock gateway normalise `tickets: []`.
- Panneau Work affiche les tickets FIFO, source Human/Agent, preview, ticket court.
- Refresh ajouté sur `delegationReady`.
- `5cb99fd``chore(wip): état runtime .ideai (flux conversation live, layouts)`
- Runtime isolé des commits feature.
- `64c2c14` — merge local dans `develop`.
## QA verte
Commandes QA réelles validées:
- `cargo fmt --all -- --check` OK.
- `cargo test -p infrastructure mailbox --lib` OK, 13 passed.
- `cargo test -p application --test workstate` OK, 12 passed.
- `cargo test -p app-tauri --test dto_agents` OK, 20 passed.
- `cargo check -p app-tauri` OK.
- `cd frontend && npx vitest run src/features/workstate/workstate.test.tsx src/features/projects/projects.test.tsx` OK, 2 files / 17 tests.
- `cd frontend && npx tsc --noEmit` OK.
- QA ajoutée: `cargo test -p domain mailbox --lib` OK, 6 passed.
Warnings observés uniquement côté Vite sur options `esbuild` dépréciées/ignorées au profit de `oxc`, non bloquants.
## Décisions produit/techniques
- Les tickets `human` et `agent` sont inclus dans le snapshot; l'UI distingue explicitement `Human` vs agent/requester au lieu de tout appeler délégation agent.
- Le statut est dérivé à la lecture: `inProgress` si le ticket courant correspond au `busy_state`, sinon `queued`.
- Pas de nouvelle persistance, pas de lecture `log.jsonl`/handoff, pas d'événement `agentQueueChanged`, pas d'action UX d'annulation/résolution.
## Suite probable
Le prochain lot logique du chantier UX conversations/délégations est Lot C: résumés de conversations depuis log/handoff en lecture best-effort, ou Lot D actions UX selon priorité produit. Suivre le cycle Main: Architect -> Git -> DevBackend/DevFrontend -> QA -> Git.

View File

@ -0,0 +1,52 @@
---
name: develop-realigned-to-cli-ui-baseline-2026-07-02
description: memory note develop-realigned-to-cli-ui-baseline-2026-07-02
metadata:
type: project
---
# develop réaligné sur la ligne UI CLI (pre-chat-ui-baseline) — 2026-07-02
**Type :** décision de topologie, validée utilisateur.
## Contexte / erreur corrigée
La feature « annonces inter-agent + fix Final Codex » avait été branchée par erreur sur
`develop` @ `0072aff`, qui portait le chantier **canonical-conversation / chat** (LC1→LC5).
Or l'UI de référence de l'utilisateur est la **ligne CLI** portée par
`feature/pre-chat-ui-baseline` @ `a9653bc` (UI CLI + commit « délégation toujours headless,
jamais d'injection PTY »). Le rebuild sur develop avait régressé l'UI (plus de terminaux CLI).
De plus, `a9653bc` — prémisse runtime de l'overlay — n'était QUE sur pre-chat-ui-baseline.
## Opération effectuée (Git, réversible, locale, aucune sortante)
1. Secours du chantier chat : branche `snapshot/develop-canonical-conversation-0072aff` +
tag homonyme → `0072aff` (récupérable à 100%).
2. `git branch -f develop feature/pre-chat-ui-baseline`**develop == `a9653bc`** (arbre
exactement celui de la ligne CLI). Le chantier chat est SUPERSÉDÉ sur la mainline develop
mais non détruit.
3. Nouvelle branche de fix : **`feature/inter-agent-announcements-v2`** depuis le nouveau
develop (@ a9653bc).
Retour arrière : `git branch -f develop snapshot/develop-canonical-conversation-0072aff`.
## État des refs
- `develop` = `a9653bc` (base de travail canonique désormais = ligne CLI).
- `feature/inter-agent-announcements-v2` = base de travail pour reporter les fix.
- `feature/inter-agent-announcements` = `71d307d` : ancien commit B0-B3 (basé sur l'ancien
develop chat), conservé pour cherry-pick.
- `snapshot/develop-canonical-conversation-0072aff` = chantier chat préservé.
## Conséquences pour la suite
- Reporter **B1/B2/B3** sur `feature/inter-agent-announcements-v2` (cherry-pick depuis
71d307d). **B0 devient probablement REDONDANT** : `a9653bc` (headless alongside/toujours
headless) est déjà la base — à vérifier avant de le rejouer.
- **Re-planifier F1/F2/F3** : le design frontend (preview cellule demandeur + overlay cellule
cible) avait été cadré contre le modèle de cellules canonical-conversation de develop ; sur
la base CLI (`a9653bc`), il faut re-cibler le modèle de cellules CLI/PTY réel. À faire
cadrer par Architect avant dev frontend.
- Rebuild AppImage à faire depuis cette base → restaure l'UI CLI + embarque les fix.
Liens : [[inter-agent-announcements-feature-and-codex-final-bug]],
[[inter-agent-live-context-shared-per-agent]].

View File

@ -0,0 +1,13 @@
---
name: f35-config-crud-delivered
description: F35.2 (config serveur local + sélection dans les profils OpenCode) livrée et verte sur feature/modeles-locaux ; clôt le sprint modèles locaux F34-F36.
metadata:
type: project
---
Ticket #35 F35.2 LIVRÉ (vert) sur `feature/modeles-locaux`. Débloqué par le contrat PLAT d'Architect.
**Contrat backend** : `list_model_servers` / `save_model_server({request:{config}})` / `delete_model_server({serverId})`. DTO plat camelCase `LocalModelServerConfig { id, kind:"llamaCpp", name, baseURL, port, modelPath?, servedModelName, binaryPath?, args, autoStart, stopPolicy }`. PIÈGE intégré : `save_model_server` exige un UUID valide dans `config.id` même à la création (backend ne génère PAS l'id serveur, seulement le model.id interne caché) → le front minte l'UUID client-side (`newModelServerId`). Erreur suppression référencée = `code:"model_server_in_use"`.
**Livré** : `ModelServerGateway` (port + adapter Tauri `adapters/modelServer.ts` + `MockModelServerGateway` avec `markInUse`). Feature `features/model-servers/` : `useModelServers` (CRUD, traduit in-use en message FR actionnable), `ModelServersPanel` (déclarer/éditer/supprimer), `ModelServerSelect` (dropdown binding avec « None » = serveur externe + préserve un id orphelin « unknown/removed »). Dans `FirstRunWizard`, le champ texte `localModelServerId` (F36) est remplacé par `ModelServerSelect`.
Cohérent avec F35.1 (badge corrèle via localModelServerId). tsc 0 ; suite 608/608. Sprint modèles locaux (F34 backend, F35, F36) désormais complet côté frontend.

View File

@ -0,0 +1,17 @@
---
name: f36-multi-opencode-profiles-frontend
description: Multiplicité des profils OpenCode locaux côté UI first-run wizard — livrée, verte, sans gap backend.
metadata:
type: project
---
Ticket #36 (F36.1) livré côté frontend sur `feature/modeles-locaux`.
**Constat clé** : aucun gap backend. Le backend supporte déjà N profils OpenCode (identité = ProfileId) via `clone_opencode_profile_from_seed`, `list_profiles`, `save_profile`, `delete_profile`, `configure_profiles`. `OpenCodeConfig` DTO expose `localModelServerId` (camelCase).
**Ce qui manquait, purement front** : la commande clone n'était câblée dans aucun gateway, et le miroir TS `OpenCodeConfig` n'avait pas `localModelServerId`.
**Livré** : `ProfileGateway.cloneOpenCodeProfileFromSeed` (port + adapter Tauri + mock) ; le first-run wizard (`FirstRunWizard.tsx` + `useFirstRun.ts`) gère désormais une LISTE de profils OpenCode — bouton "Add OpenCode profile" + "Duplicate" par ligne, name éditable, cases reasoning/attachment, champ localModelServerId (saisie simple).
**Reste** : lot F35 = UI riche de config serveur local (remplacera la saisie simple de `localModelServerId`). Le wizard reste la surface de gestion de profils (`Settings ▸ Configure profiles` = `forceOpen`) ; il lit `firstRunState().referenceProfiles`, pas `list_profiles` — à vérifier au lot F35 pour l'édition de profils OpenCode déjà persistés.
tsc propre ; 62 tests first-run/mock verts ; suite complète 581/581.

View File

@ -0,0 +1,11 @@
---
name: floatingwindow-focus-trap-mount-only
description: Le focus-trap de FloatingWindow ne doit dépendre que du mount ; sinon un onClose instable vole le focus à chaque frappe.
metadata:
type: reference
---
Le primitif `frontend/src/shared/ui/FloatingWindow.tsx` a un `useEffect` focus-trap qui appelle `(first ?? node)?.focus()`. Sa dépendance doit rester `[]` (mount-only), et `onClose` doit être lu via un `onCloseRef` maintenu à jour.
**Pourquoi** : les appelants (ex. `TicketDetail.requestClose`) passent souvent un `onClose` recréé à chaque render. Si l'effet dépend de `[onClose]`, chaque frappe dans un input contrôlé de la fenêtre → re-render → nouvelle identité `onClose` → l'effet se ré-arme → focus renvoyé au premier focusable → l'utilisateur ne peut taper qu'UNE lettre à la fois (bug #17, corrigé 2026-07-07).
**Comment l'appliquer** : ne jamais remettre `onClose` (ni une autre prop changeante) dans les deps de cet effet ; router les callbacks via une ref. Régression couverte par le test « #17 focus-loss bug » dans FloatingWindow.test.tsx. Voir [[ticket16-menus-floating-delivered-devfrontend]].

View File

@ -0,0 +1,14 @@
---
name: frontend-uses-npm-not-pnpm
description: memory note frontend-uses-npm-not-pnpm
metadata:
type: project
---
Le dossier `frontend/` s'installe et se build avec **npm**, jamais pnpm (lockfile `package-lock.json`).
**Ne jamais lancer `pnpm` (ni `corepack pnpm build`, ni le wrapper `pnpm --dir frontend build`) dans `frontend/`** :
- `pnpm --dir frontend build` échoue d'abord sur un pré-check `pnpm install` (`ERR_PNPM_IGNORED_BUILDS` sur esbuild).
- Surtout, `pnpm install` **clobber** le `node_modules` npm : pnpm aplatit `vite@5.4.21` au top-level alors que `vitest@4.1.x` exige `vite ^6||^7||^8`. Résultat : `ERR_PACKAGE_PATH_NOT_EXPORTED: './module-runner'` dès que vitest spawn son worker pool (>3 fichiers de test). Avec npm, `vite@8` est imbriqué sous `node_modules/vitest/node_modules/vite`, donc tout marche.
- Réparation si le clobber arrive : `rm -f frontend/pnpm-lock.yaml frontend/pnpm-workspace.yaml && cd frontend && npm install`, puis `git checkout -- frontend/package-lock.json`.
Commandes correctes sans le wrapper cassé : `cd frontend && npx tsc --noEmit && npx vite build` (build) et `npx vitest run` (tests). Le script `pnpm build` du package.json ne doit pas être utilisé tel quel dans cet environnement.

View File

@ -0,0 +1,19 @@
---
name: idea-distribution-strategy-desktop-vs-docker
description: memory note idea-distribution-strategy-desktop-vs-docker
metadata:
type: project
---
Stratégie de distribution IdeA validée utilisateur (2026-07-16, suite #13) : DEUX offres au-dessus du MÊME cœur backend (pas de fork métier).
**Offre 1 — Full desktop** : binaire Tauri `app-tauri` actuel. AppImage Linux (existe, inchangée). Windows explicitement REPORTÉ (garder la portabilité à l'esprit, ne rien introduire de non-portable ; PTY = ConPTY à retester le jour venu). L'AppImage reste desktop PUR — elle ne bundle pas les assets web.
**Offre 2 — Serveur/client** : image Docker, bâtie sur un binaire serveur HEADLESS `idea-serve` SANS dépendance Tauri/WebKit (pas de dockerisation du binaire Tauri qui traînerait GTK/WebKit pour rien). Cadré faisable en hexagonal par Architect.
**Point pivot technique** : `server.rs` vit dans `crates/app-tauri` et réutilise indirectement la présentation Tauri via `crate::state::AppState`, `crate::dto::*`, `crate::events::DomainEventDto`, `crate::pty::PtyChunk`, `crate::mcp_endpoint::*`, `ResumeContext`. Le protocole HTTP/WS lui est déjà autonome. Le vrai travail = découpler les DTO (→ crate partagé `presentation-dto`/`backend-api`) puis extraire `crates/web-server` (sur `BackendCore`, pas `AppState`) puis le bin `idea-serve`.
**Tickets** : #65 (headless, high, L1 DTO→L2 web-server→L3 idea-serve), #66 (Docker, medium, L4 build web http→L5 Dockerfile volumes /data+/workspace→L6 agents CLI conteneur), #64 (folder browser web, dependsOn #66 pour /workspace), #67 (lock inter-process app-data-dir, low).
**Topologie de branches (décision utilisateur)** : chantier packaging sur `feature/server-client-packaging` (créée depuis `c246875`, tête de `feature/ticket13-pty-websocket`). PAS de merge dans develop tant que l'utilisateur n'a pas validé lui-même la NON-RÉGRESSION desktop-only. Flux final : `feature/server-client-packaging``feature/ticket13-pty-websocket``develop`. Git rebase la branche packaging si #13 avance.
Invariants : desktop AppImage ne perd RIEN ; aucun import Tauri dans le bin headless ; même cœur/use cases/stores ; contrat HTTP/WS inchangé sauf lot versionné. Voir [[frontend-uses-npm-not-pnpm]], [[appimage-build-no-strip-relr-dyn-fix]].

View File

@ -0,0 +1,67 @@
---
name: inter-agent-announcements-feature-and-codex-final-bug
description: memory note inter-agent-announcements-feature-and-codex-final-bug
metadata:
type: project
---
# Annonces inter-agent (UI live) + fix du Final Codex — DESIGN FIGÉ
**Type :** design de feature + bug racine. Validé utilisateur le 2026-07-02, cadré par Architect.
## Bug racine (Codex)
`crates/infrastructure/src/session/codex.rs` : `parse_event` (~l.86) mappe CHAQUE
`item.completed{item.type=="agent_message"}` vers `ReplyEvent::Final`, et `send` (~l.234)
casse au PREMIER Final (`break 'lines`). Or `codex exec --json` émet plusieurs
`agent_message` par tour (préambule → outils → conclusion → `turn.completed`). IdeA
renvoyait donc le **préambule** au demandeur au lieu de la **conclusion**. Preuve live :
Architect répondait « je vais interroger QA… » au lieu du résultat. **Défaut d'implémentation,
pas du headless Codex** (un `codex exec` brut déroule tout le tour).
## Feature validée (le bug devient fonctionnalité)
- **Final** = DERNIER `agent_message` avant `turn.completed` → seule chose renvoyée au
demandeur (résout le ticket). Bords : 1 seul message (préambule=conclusion) ; 0 message
`TargetReturnedNoReply` (inchangé).
- **Annonces** = `agent_message` intermédiaires → nouvel événement NON terminal
`ReplyEvent::Announcement{text}`, poussé sur le bus vers l'UI, JAMAIS renvoyé au
demandeur, JAMAIS persisté au transcript (éphémère).
- **UI** : (a) cellule DEMANDEUR = petit cadre preview près de la drop-list d'agents,
filtré par `ticket_id` ; (b) cellule CIBLE si visible = overlay d'indisponibilité (texte
haut « un agent est en train de lui parler » + annonces défilantes au centre), monté sur
live-state Working, retiré au Final/idle (reste tant qu'≥1 ticket actif sur la cible).
## Contrat (Architect)
- `ReplyEvent::Announcement{text}` (non terminal) vs `Final{text}` (terminal, résout).
- `drain_with_readiness` : Announcement → publie event, ne résout pas, ne passe pas idle ;
Final → résout ticket + idle + fin du drain.
- Nouveau `DomainEvent::AgentAnnouncement{project_id, requester, target, ticket_id, text,
at_ms}` (attribution obligatoire pour router vers la bonne cellule + isoler projet/onglet).
- Overlay cible = état UI piloté par live-state (pas un blocage du PTY : le contact passe par
session headless séparée, `allow_structured_alongside_pty:true`).
- Généralisation Claude : `session/claude.rs` garde son terminal explicite (`result`) comme
Final ; messages complets non terminaux → Announcement ; NE PAS promouvoir les `TextDelta`
tokenisés (bruit) ; tool events restent `ToolActivity`.
## Découpage dev (Architect)
- **B1** domaine/appli : `ReplyEvent::Announcement`, `DomainEvent::AgentAnnouncement`,
`structured.rs`, `service.rs` (`ask_structured` ne termine que sur Final).
- **B2** Codex `codex.rs` : bufferiser le dernier `agent_message`, émettre les précédents en
Announcement, le dernier en Final à `turn.completed`.
- **B3** Tauri `app-tauri/{events,dto,state}.rs` : relay + DTO camelCase.
- **F1** front store/gateway annonces (index par ticket/target, borné ~20-50).
- **F2** front preview cellule demandeur.
- **F3** front overlay cellule cible.
- **Q** QA e2e : Main→Architect→QA, préambule+outil+conclusion ; Main ne reçoit que la
conclusion ; preview demandeur + overlay cible pendant le travail ; fermeture au Final.
Critère : reproduire le bug initial et constater la correction.
## État
Design figé, cadrage Architect complet. Reste : Git branche → B1/B2/B3 + F1/F2/F3 → QA.
Non démarré côté dev au 2026-07-02.
Liens : [[inter-agent-live-context-shared-per-agent]], [[rendezvous-no-reply-backstop-design]],
[[headless-interagent-conversation-objective]], [[conversation-viewer-ls7-frontend]].

View File

@ -0,0 +1,79 @@
---
name: inter-agent-live-context-shared-per-agent
description: memory note inter-agent-live-context-shared-per-agent
metadata:
type: project
---
# Contexte live inter-agent : PAR AGENT (partagé), pas par paire — DÉCISION FIGÉE
**Type :** decision d'architecture / produit — validée utilisateur le 2026-07-02.
## Décision
Le contexte live d'un agent IdeA est porté **par l'agent cible**, partagé entre TOUS les
demandeurs. Pour un `AgentId` donné, IdeA maintient **au plus une** session
headless/structurée vivante ; tous les `idea_ask_agent(requester, target)` vers le même
`target` réutilisent cette session, quel que soit le demandeur.
Ce partage est **intentionnel et souhaité**, pas un bug ni une fuite : tous les agents du
projet sont dans le même domaine de confiance (même utilisateur, même projet). Le contexte
live d'un agent = mémoire de travail d'équipe / tableau blanc partagé. On NE veut PAS
d'isolation par paire.
La paire `(requester, target)` n'est **pas** une frontière d'isolation du contexte moteur :
elle sert uniquement à l'attribution, la corrélation requête/réponse, les vues UI et les
filtres de transcript.
## Preuve empirique (2026-07-02)
Main a confié à QA le token `IDEA-TOKEN-7F3A-BLEEDCHECK` ; Architect a ensuite pu le
récupérer auprès de QA **sans que Main le lui transmette**. Cause : `ensure_structured_session`
(`crates/application/src/orchestrator/service.rs:1948`) fait un early-return sur
`session_for_agent(&agent_id)` → une seule session vivante par agent, réutilisée.
## Modèle de transcript cible (cadré par Architect)
**Log canonique PAR AGENT + vues par paire dérivées.** Abandon du transcript canonique
par paire comme source de vérité (il promet une isolation qui n'existe pas en live).
- Arbo cible : `.ideai/conversations/agents/<targetAgentId>/{log.jsonl, handoff.md, providers.json, log.N.jsonl}`
- Chaque tour porte l'attribution : `thread_id=Agent(target)`, `requester`, `target`,
`correlation_id`, `role`, `source`.
- Vues = projections filtrées (`ForAgent`, `ForPair`, `ForCorrelation`), pas des logs séparés.
Impacts à traiter :
- `resolve_conversation``resolve_agent_thread(target)` + `resolve_conversation_view(requester,target)`.
- `bind_conversation_session` → bind sur `targetAgentId` ; réutiliser la session existante.
- `ConversationRegistry` → index primaire `targetAgentId`, secondaires `requester`/`correlation_id` ;
interdire 2 fils live pour le même target.
- `ProviderSessionStore` clé `(agent_thread_id, provider_id)` au lieu de `(pair_id, provider_id)`.
- `LeafCell.conversation_id` = désormais **id de fil agent**, plus « id de paire ».
- Viewer LS7 : défaut = fil agent complet ; vue par paire = vue filtrée/partielle libellée comme telle.
## Garde-fou efficience (rotation multi-demandeurs)
Le design LS5/LS6 tient, mais un fil agent partagé **croît plus vite** (agrège les demandes
de plusieurs requesters). Ajustements : la rotation s'applique au fil agent canonique (pas
aux vues) ; le résumé/handoff doit conserver l'attribution minimale (`Objectif courant`,
`Demandes actives par requester`, `Décisions récentes`, `Blocages`, `Dernière réponse
corrélée`) ; transcript brut jamais réinjecté (humain-only) ; handoff borné à
`HANDOFF_SUMMARY_MAX_CHARS=4096`. Risque principal : dilution si demandeurs poussent des
objectifs contradictoires → mitigé par les rubriques stables du handoff.
## Découpage dev recommandé (Architect)
1. `ARCHITECTURE.md` §19.7 : remplacer « id de paire » par « fil agent partagé » (+ §16/§17/§21).
2. Introduire `ConversationThreadId` / `ConversationViewId`.
3. Adapter `resolve_conversation`, `bind_conversation_session`, `ConversationRegistry`.
4. Compatibiliser les anciens chemins `for_pair(requester,target)` comme vues historiques.
5. LS7 viewer : fil agent complet + filtres.
6. Test QA : un fait confié à QA par Main est visible quand Architect interroge QA.
## État
Décision figée. Cadrage architectural livré par Architect. Reste à : intégrer la proposition
dans `ARCHITECTURE.md`, puis planifier le dev (cycle normal Architect→Git→Dev→QA).
Liens : [[conversation-rotation-safety-design]], [[handoff-ls5-summary-bound-and-llm-seam]],
[[conversation-log-ls6-rotation-and-paginated-read]], [[conversation-viewer-ls7-frontend]],
[[headless-interagent-conversation-objective]].

View File

@ -0,0 +1,78 @@
---
name: issue-ticket-system-design
description: memory note issue-ticket-system-design
metadata:
type: project
---
---
name: issue-ticket-system-design
description: Cadrage figé (Architect+Main, 2026-07-02) du système de tickets IdeA façon Jira — domaine `Issue` par projet, exposé « ticket » côté UI/MCP, Carnet éditable, #N séquentiel. Fait autorité pour les lots T1-T6/F1-F7.
metadata:
type: reference
---
# Système de tickets IdeA (domaine `Issue`) — CADRAGE FIGÉ 2026-07-02
## Décisions produit verrouillées (utilisateur)
- **Portée PAR PROJET** : stockage dans `.ideai/tickets/` du projet (pas de backlog global).
- **Identifiant court `#42`** : compteur séquentiel par projet, parlable à l'oral (poignée
partagée humain↔agent, y compris dans une délégation `idea_ask_agent`).
- **Champ de connaissances éditable = « Carnet »** (PAS « mémoire » — évite collision avec la
mémoire projet). Markdown éditable/réorganisable, scoped ticket, pas append-only.
- **Stockage Markdown + frontmatter**, un DOSSIER par ticket.
- **V1 SANS attachments** : livrer T1-T5 + F1-F5 + F7 ; attachments (T6/F6) en fast-follow.
## Nommage (collision évitée avec le TicketId de délégation)
Domaine/code = **`Issue`** (`IssueId`, `IssueNumber`, `IssueRef("#42")`, `IssueStore`,
modules `domain::issue`/`application::issues`/`infrastructure::issues`). UI + MCP public =
« ticket » (`idea_ticket_*`, DTO camelCase). Ne JAMAIS nommer `Ticket` en code (réservé à la
délégation inter-agent).
## Frontière Carnet vs mémoire projet
Carnet = savoir DU ticket (décisions locales, hypothèses, investigation, contraintes). Mémoire
projet = savoir transverse durable. Règle : si l'info doit survivre à la fermeture du ticket et
guider d'autres travaux → mémoire projet ; sinon → carnet.
## Modèle domaine
`Issue { id(uuid), number(u64), title, description(md), status, priority, carnet, links,
agent_refs, attachments, created_by/updated_by(IssueActor: User|Agent|System),
created_at/updated_at, version(optimistic) }`.
- `IssueStatus`: Open | InProgress | Qa | Closed (DTO: open|inProgress|QA|closed).
- `IssuePriority`: Low | Medium | High | Critical.
- `IssueLinkKind`: RelatesTo | Blocks | BlockedBy | Duplicates | DependsOn.
- `AgentIssueRef { agent_id, role: Assigned|Mentioned|Reviewer|Owner }`.
Invariants : number>0 unique/projet ; IssueRef = `#<u64>` ; titre non vide ; pas de self-link ;
assignation → AgentId connu du manifeste ; toute écriture incrémente version ; attachments sous
le dossier du ticket, pas de `..`/symlink/chemin absolu.
## Ports
`IssueStore` (create/get_by_ref/list(filter)/update(expected_version)/read_carnet/
write_carnet(expected_version)) ; `IssueNumberAllocator` (allocate_next, atomique via
counter.json + counter.lock + rename, jamais de réutilisation de numéro) ; `IssueAttachmentStore`
(add/remove/list — lot attachments). Adapters : `FsIssueStore`, `FsIssueNumberAllocator`,
`FsIssueAttachmentStore`, `McpIssueTools`, `TauriIssueCommands`, `TauriIssueEventRelay`.
## Surface MCP (agents)
`idea_ticket_create/read/list/update/update_status/update_priority/read_carnet/update_carnet/
link/unlink` (+ `idea_ticket_attachment_*` au lot attachments). DTO camelCase ; `TicketRef` =
`#${number}` ; concurrence via `expectedVersion` sur chaque écriture.
## Events domaine (→ projection UI, même modèle events→front B-series)
IssueCreated/Updated/StatusChanged/PriorityChanged/CarnetUpdated/Linked/Unlinked/
AgentAssigned/AgentUnassigned/AttachmentAdded/Removed/StorageConflictDetected.
## Layout stockage
`.ideai/tickets/{counter.json, index.json(projection reconstructible), <N>/{issue.md(frontmatter
+description), carnet.md, attachments/}}`. Attachments versionnés git par défaut (warning >5MiB,
refus configurable >25MiB, pointeur URI pour les gros).
## Lots
Backend : T1 domaine Issue · T2 store FS Markdown (+allocator) · T3 use cases (Create/Read/List/
Update/UpdateCarnet/Link/AssignAgent, optimistic concurrency) · T4 surface MCP `idea_ticket_*` ·
T5 commands Tauri + relay events · T6 attachments (fast-follow).
Frontend : F1 gateway UI (`IssueGateway`, pas d'invoke direct) · F2 liste filtrable · F3 détail/
édition (gestion conflits expectedVersion) · F4 éditeur Carnet Markdown · F5 liens & assignations ·
F6 attachments (fast-follow) · F7 `#42` cliquable + pré-remplissage délégation.
Réutilise : store FS par projet façon `FsBackgroundTaskStore`/`AppLiveStateProvider`, EventBus +
TauriEventRelay, pattern gateway UI.
Lien : [[background-tasks-first-class-design]], [[agent-context-memory-and-profile-handoff]].

View File

@ -1,47 +0,0 @@
---
name: mcp-e2e-findings-reply-wedge-phantom-busy
description: memory note mcp-e2e-findings-reply-wedge-phantom-busy
metadata:
type: project
---
---
name: mcp-e2e-findings-reply-wedge-phantom-busy
description: Findings validation MCP e2e live (AppImage 0.3.0) — transport/rendez-vous SAINS ; un seul vrai défaut = wedge si l'agent délégué n'appelle pas idea_reply (pas de timeout serveur).
metadata:
type: project
---
# Findings validation MCP e2e (live, AppImage 0.3.0) — 2026-06-22
Contexte : validation réelle du parcours inter-agents (cf. [[resume-after-appimage-rebuild-mcp-e2e]]). Méthode = Main observe en externe (Bash sur `ss -xp` + transcripts `~/.claude/projects/<run-dir-encodé>/*.jsonl`), l'utilisateur déclenche dans l'UI ; on ne valide PAS via `idea_ask_agent`.
## Scénario 1 — délégation vers agent FROID + cycle complet : VERDICT
-**Réveil à froid** : déléguer vers un agent arrêté relance son pont (`mcp-server --requester <id>` réapparaît, socket ESTAB recompte).
-**1er tour non perdu** : tâche livrée intacte = 1er `user` `[IdeA · tâche de <agent> · ticket <uuid>] …`.
-**Chemin positif `idea_reply`** : quand l'agent cible appelle `idea_reply(ticket,result)`, le bridge répond `reply ... delivered`, le **demandeur reçoit le `tool_result` et reprend la main** (vérifié : DevBackend reçoit `pong` puis continue).
-**Purge live-state** : dès l'`idea_reply`, `idea_workstate` repasse le cible de `working``done`, intent vidé.
➡️ **Transport + protocole de rendez-vous = SAINS.** Bugs historiques 3/4/5 OK sur ce chemin.
## LE défaut restant (racine unique) — wedge sans `idea_reply`
Si l'agent délégué **termine son tour sans appeler `idea_reply`** (ex. tâche triviale « réponds pong » → il répond `pong` en **prose** et s'arrête) :
- le **demandeur wedge indéfiniment** dans `idea_ask_agent` (transcript demandeur figé sur le `tool_use` idea_ask_agent, aucun `tool_result`) ;
- la **cible reste `working`** sur le ticket périmé dans la live-state ;
- **aucun timeout ni récupération** côté serveur. Une interruption utilisateur du `ask` libère le demandeur mais **ne purge pas** le `working` de la cible (purge seulement à l'`idea_reply`).
➡️ Le « Busy fantôme » n'est PAS un bug d'état indépendant : c'est la **conséquence** de l'absence d'`idea_reply`. Une seule cause à traiter.
### Pistes correctif (à arbitrer Architect)
1. **Durcir contextes agents** : « toute tâche déléguée se termine IMPÉRATIVEMENT par `idea_reply`, même triviale » (cheap, attaque la cause comportementale).
2. **Garde-fou serveur (rendez-vous)** : timeout sur l'attente `idea_ask_agent` → renvoyer au demandeur un résultat d'échec/relance explicite + purger le `working` de la cible (idempotent). Touche le contrat rendez-vous + cycle de vie live-state = domaine Architect. Voir [[idea-program-surface-separation-and-livestate]], [[mcp-bridge-and-delegation-runtime-notes]].
## Diagnostic / repro (rappels)
- Ponts : `pgrep -af 'mcp-server --endpoint'` (1 requester = 2 process) ; `ss -xp | grep -c idea-mcp`.
- Reply réel = `tool_use` `idea_reply` dans le transcript cible → tool_result `reply ... delivered` (⚠️ ne pas confondre avec les mentions `idea_reply` du CLAUDE.md injecté via attachments).
- Wedge demandeur = transcript demandeur s'arrête sur `tool_use` `idea_ask_agent` sans `tool_result`.
- ⚠️ Horodatages transcripts = **UTC** ; `ls %H:%M:%S` masque la date → comparer avec `date` + mtime réel (`ls -t`).
## Restant à tester (scénarios e2e suivants)
- Délégation vers agent BACKGROUND (node_id None) — même protocole, vérifier pas de 1er tour perdu.
- `ask` sans réponse ne wedge pas durablement la CONNEXION du demandeur (vs juste l'appel) — Bug 5 côté transport.
- Profils Claude/Codex utilisent MCP par défaut ; fallback fichier+prose cohérent.
- Observabilité UI des délégations/replies (aujourd'hui opaque) — sujet UX.

View File

@ -1,45 +0,0 @@
---
name: mcp-functional-test-plan-2026-06-24
description: memory note mcp-functional-test-plan-2026-06-24
metadata:
type: project
---
---
name: mcp-functional-test-plan-2026-06-24
description: Plan de test FONCTIONNEL du rendez-vous inter-agents MCP (trivial→tordu), à exécuter après rebuild develop HEAD ; méthode, ordre, setup IdeA et critères de réussite.
metadata:
type: project
---
# Plan de test fonctionnel MCP inter-agents (2026-06-24, run a6ced819)
Objectif : état des lieux par tests **fonctionnels** (pas unitaires) du rendez-vous `idea_ask_agent``idea_reply`, du trivial au plus tordu. On lance dans l'ordre, on **s'arrête au 1er blocage**, on corrige (cycle Architect→Git→Dev→QA), rebuild+relance, puis on **reprend de T1**. Contexte/historique : [[mcp-e2e-findings-reply-wedge-phantom-busy]], [[backstop-fires-on-intra-task-turn-rootcause]], [[rendezvous-no-reply-backstop-design]], [[rendezvous-600s-cap-too-short-heavy-tasks]], [[reconcile-live-state-implemented-feature-branch]].
## Contexte binaire au moment du plan
- AppImage qui tournait = build **23/06 19:42**, PÉRIMÉ (antérieur à `c6f0f86` encode_cwd, `744de20` backstop no-reply, `886bb0d`/`1efe2f1` reconcile). Décision utilisateur : **rebuild develop HEAD `1efe2f1` puis relance** avant de tester.
- Règle structurelle : **binaire qui tourne = AppImage**. Tout fix backend ⇒ rebuild + relance d'IdeA (tue Main). Les tests s'enchaînent SANS relance *à l'intérieur d'une même version* ; chaque cycle de fix = 1 relance.
## Méthode
- **Main est le demandeur** (`idea_ask_agent`) = test fidèle de « si je donne un message à un agent, il me répond ».
- Doublé d'**observation externe Bash** : ponts `ss -xp | grep idea-mcp` et `pgrep -af mcp-server` ; transcripts `~/.claude/projects/<run-dir-encodé>/*.jsonl` (horodatages **UTC** ; reply réel = `tool_use` idea_reply→tool_result `reply … delivered` ; wedge demandeur = transcript figé sur `tool_use` idea_ask_agent sans tool_result) ; live-state via `idea_workstate_read`.
- Risque résiduel : un wedge non borné fige le tour de Main → l'utilisateur peut interrompre.
## Setup IdeA à mettre en place avant T1 (consigne utilisateur)
1. Relancer IdeA, ouvrir le projet IdeA, **Main visible** (le demandeur).
2. Garder **23 cellules libres visibles** pour qu'on observe à l'œil les délégations (Main y placera des cibles visibles, ou lance-toi QA + DevFrontend dans des cellules visibles).
3. **Laisser Architect, DevBackend, Git ARRÊTÉS (froids)** : T3 testera le réveil à froid ; T9 utilisera Architect+DevBackend froids.
4. Cibles chaudes pour T1/T2 : Main les réchauffe lui-même (launch) — pas d'action requise.
## Ordre des tests (trivial → tordu)
- **T1 — Visibilité outils MCP.** Un agent lancé par IdeA voit ses outils sans action manuelle. Vérif : pont ESTAB (`ss`) + 2 process mcp-server par requester.
- **T2 — Cible CHAUDE + idea_reply trivial.** Main→ask(agent chaud, « réponds pong via idea_reply »). Attendu : réponse inline `pong`, temps borné, workstate working→done purgé.
- **T3 — Réveil à FROID + idea_reply.** Cible arrêtée. Attendu : pont relancé (nouveau mcp-server), **1er tour non perdu** (1er user `[IdeA · tâche…]` intact), reply remonte.
- **T4 — Cible BACKGROUND (node_id None).** Délégation vers agent sans cellule visible. Attendu : même protocole, pas de 1er tour perdu (historiquement non testé).
- **T5 — NO-REPLY (cible répond en prose, pas d'idea_reply).** Attendu backstop : demandeur libéré en temps borné avec `TargetReturnedNoReply` (retryable), **pas de Busy fantôme** (workstate purgé). DÉFAUT historique #1.
- **T6 — Délégation MULTI-ÉTAPES puis idea_reply.** Cible lit plusieurs fichiers / lance une cmd PUIS idea_reply. Attendu : **pas de libération prématurée** pendant le travail, rapport final livré. PIÈGE turn-watcher (cause racine : backstop tirait au 1er tour interne ~3 s).
- **T7 — Tâche LOURDE > plafond** (impl + `cargo test`/clippy). Attendu : extension sur signe de vie OU message clair « cible active, plafond atteint » (≠ faux `-32001`) ; idea_reply tardif non perdu. Plafond `IDEA_ASK_RENDEZVOUS_TIMEOUT_MS` (défaut 600000).
- **T8 — Délégations PARALLÈLES** (2 cibles en 1 tour Main). Attendu : rendez-vous multiples simultanés OK, multiplexage pont, 2 réponses distinctes.
- **T9 — TRANSITIVE A→B→C** (Main→Architect, Architect délègue à DevBackend puis reply à Main). Attendu : rendez-vous imbriqués, 2 attentes simultanées sans deadlock. Cas le plus tordu.
- **T10 — INTERRUPTION/annulation + reconcile reboot.** (a) Main interrompt un ask en vol → `working` cible purgé, pas de cascade, idea_reply tardif non-livrable proprement. (b) Au reboot, orphelins status∈{working,waiting,blocked} & session morte → `idle` + marker `STALE_AT_RESTART_MARKER` (use case ReconcileLiveState, cf. [[reconcile-live-state-implemented-feature-branch]]).
## Critère de fin
Tous les T1→T10 verts avec preuve réelle (réponse inline + observation externe cohérente). Sinon : 1er rouge = blocage à corriger, puis reprise de T1.

View File

@ -1,33 +0,0 @@
---
name: mcp-functional-tests-t1-t9-green-live-2026-06-24
description: memory note mcp-functional-tests-t1-t9-green-live-2026-06-24
metadata:
type: project
---
---
title: MCP tests fonctionnels T1→T9 VERTS en live (2026-06-24, AppImage 12:53 HEAD 1efe2f1 + fix T7 non committé)
type: project
description: Passe complète T1→T9 verte en conditions réelles sur AppImage du 24/06 12:53 ; T5 (backstop no-reply) et T7 (watchdog inactivité >600s) validés live ; reste T10 (interruption + reconcile reboot) qui exige action utilisateur.
---
# Tests fonctionnels MCP rendez-vous — T1→T9 VERTS live (2026-06-24)
Run a6ced819, plan [[mcp-functional-test-plan-2026-06-24]]. AppImage **rebuild 12:53 depuis l'arbre de travail** (HEAD `1efe2f1` + le fix T7 watchdog **non committé** mais présent dans les 11 fichiers modifiés du git status → embarqué dans le binaire). Méthode : Main demandeur + observation externe (ss/pgrep/transcripts UTC/workstate).
## Résultats (preuve réelle)
- **T1 visibilité MCP** ✅ : agents chauds = pont ESTAB + 2 mcp-server (app-tauri parent + AppImage) chacun.
- **T2 cible chaude + reply trivial** ✅ : DevFrontend → `pong` inline, reply delivered 10:56:51 UTC, workstate done (pas de Busy fantôme).
- **T3 réveil à FROID** ✅ : Git froid → `pong-cold` ; nouveau pont spawné, **1er tour `[IdeA·tâche]` intact** (régresseur encode_cwd résolu), reply delivered 10:57:34.
- **T4 cible BACKGROUND (node_id None)** ✅ : Git background → `pong-bg`, reply delivered 10:58:14.
- **T5 NO-REPLY backstop** ✅ (défaut historique #1 résolu) : cible répond en prose sans idea_reply → demandeur libéré en ~20 s avec erreur retryable « returned to its prompt without calling idea_reply », workstate cible done (PAS de Busy fantôme).
- **T6 multi-étapes** ✅ (piège turn-watcher résolu) : DevFrontend lit fichiers + git log PUIS idea_reply ; pas de libération prématurée ; données réelles (142 fichiers, hashes corrects).
- **T7 tâche LOURDE >600s** ✅ (défaut historique #2 résolu, validation live du fix watchdog) : QA charge réelle continue 11:00:46→11:12:44 UTC = **~11m58s**, cargo clean+build release+test (1632 passed/0)+clippy+9 relances ; rendez-vous **NON expiré à 600s**, idea_reply accepté au-delà du plafond (aucun « no pending request »), reply delivered 11:13:21.
- **T8 PARALLÈLE** ✅ : 2 asks même tour (DevFrontend `alpha-8` + Git `beta-8`), réponses distinctes, multiplexage pont OK.
- **T9 TRANSITIF A→B→C** ✅ : Main→Architect→DevBackend (les 2 froids) → `relay:gamma-9:done` ; rendez-vous imbriqués + double réveil à froid sans deadlock ; DevBackend reply delivered 11:14:32.
## Reste : T10 (exige action utilisateur)
- **T10a interruption** : Main interrompt un ask en vol → attendu : workstate cible purgé, pas de cascade, idea_reply tardif proprement non-livrable. NÉCESSITE que l'utilisateur appuie sur interrupt pendant un ask wedgé.
- **T10b reconcile reboot** : orphelin status∈{working,waiting,blocked} + session morte → au reboot, use case ReconcileLiveState le passe à idle + `STALE_AT_RESTART_MARKER` (cf. [[reconcile-live-state-implemented-feature-branch]]). NÉCESSITE un reboot d'IdeA (ferme Main).
## Suite recommandée
1. Faire committer le fix T7 par Git (encore non committé, cf. [[git-owns-commit-merge-decisions]] + [[rendezvous-600s-cap-too-short-heavy-tasks]]).
2. Exécuter T10 avec l'utilisateur (interrupt + reboot).

View File

@ -1,29 +0,0 @@
---
name: mcp-t10a-harness-interrupt-does-not-cancel-rendezvous
description: memory note mcp-t10a-harness-interrupt-does-not-cancel-rendezvous
metadata:
type: project
---
---
title: T10a — l'interrupt harness du demandeur n'annule PAS le rendez-vous backend (reply tardif accepté dans le vide)
type: reference
description: Finding T10a (2026-06-24) — interrompre Main (demandeur) au niveau Claude Code ne propage aucune annulation à IdeA ; la cible finit, son idea_reply est « delivered » dans un demandeur abandonné, et la live-state du demandeur reste working.
---
# Finding T10a (2026-06-24, run a6ced819)
## Setup
Main → `idea_ask_agent(DevFrontend, "travaille ~90s puis idea_reply late-10a")`. Pendant l'attente, l'utilisateur **interrompt Main** (Échap harness Claude Code). Le tool use `idea_ask_agent` est rejeté côté harness.
## Observé
- DevFrontend a fini ses ~95 s de travail réel (11:20:39→11:22:14 UTC) puis `idea_reply("late-10a")`**ACCEPTÉ « delivered »** à 11:22:18 (PAS « no pending request »).
- DevFrontend workstate = **done** (pas d'orphelin côté cible). Pas de cascade.
- **Main (demandeur) workstate resté `working`** : l'interrupt a coupé le tour de Main avant son nettoyage ; le chemin interrupt ne réconcilie pas la live-state du demandeur.
## Interprétation
L'interrupt Claude Code annule le **tool call local** mais le **process Main survit** → la socket MCP (mcp-server du demandeur) reste connectée → IdeA ne reçoit aucun signal d'annulation → le rendez-vous reste pending → le reply tardif de la cible est livré dans un demandeur qui a déjà abandonné (perdu pour Main mais rapporté « delivered »).
## Conséquence
- Le T10a du plan (« annulation en vol → reply tardif non-livrable proprement ») **n'est pas exerçable via interrupt harness** tant que le process demandeur survit. Une vraie annulation exigerait : tuer le process demandeur (drop socket) OU un cancel protocolaire JSON-RPC.
- Comportement néanmoins bénin : pas de wedge, pas de cascade, cible réconciliée (done). Seuls écarts : (a) reply tardif « delivered » au lieu de rejeté ; (b) live-state du demandeur laissée working par le chemin interrupt (mais reconcile-au-reboot la rattraperait, cf. [[reconcile-live-state-implemented-feature-branch]]).
Lié à [[mcp-functional-tests-t1-t9-green-live-2026-06-24]], [[mcp-functional-test-plan-2026-06-24]].

View File

@ -1,24 +0,0 @@
---
name: mcp-t10b-pending-reboot-verification
description: memory note mcp-t10b-pending-reboot-verification
metadata:
type: project
---
---
title: T10b PASS — reconcile au reboot vérifié, inclut l'orchestrateur (2026-06-24)
type: project
description: T10b validé live après reboot. ReconcileLiveState ramène l'orphelin Main working→idle en conservant l'intent et purgeant ticket/lastDelegation. Couvre l'orchestrateur. Reste : faire committer par Git les fix non committés (T7 watchdog + reconcile).
---
# T10b — PASS (reboot reconcile vérifié)
## Verdict
Orphelin armé = Main `working` intent "T10b-ORPHAN-MAIN". Après reboot (run a6ced819), `idea_workstate_read` montre Main :
- `status: idle` ✅ (était working)
- `intent` conservé ✅
- `ticket` purgé ✅ / `lastDelegation` purgé ✅
Signature `STALE_AT_RESTART_MARKER` complète. **ReconcileLiveState valide ET inclut l'orchestrateur** (Main n'a PAS été exclu malgré la crainte du design open_project). Champ `progress` non exposé par workstate_read mais transition working→idle+intent retenu = concluante.
## Suite du plan
T1→T10 du plan [[mcp-functional-test-plan-2026-06-24]] terminés (T1→T9 verts cf. [[mcp-functional-tests-t1-t9-green-live-2026-06-24]], T10a finding [[mcp-t10a-harness-interrupt-does-not-cancel-rendezvous]], T10b PASS ici).
**RESTE : faire committer par Git les fix non committés (T7 backstop/watchdog + reconcile live-state).** cf. [[git-owns-commit-merge-decisions]].

View File

@ -1,37 +0,0 @@
---
name: mcp-t7-backstop-600s-reply-rejected-livestate-stale
description: memory note mcp-t7-backstop-600s-reply-rejected-livestate-stale
metadata:
type: project
---
---
title: MCP T7b/T7e — backstop 600s rejette le reply tardif et laisse le live-state worker non réconcilié
type: reference
description: Finding fonctionnel des tests MCP T7b/T7e (2026-06-24) — un idea_reply après dépassement de la fenêtre 600s est refusé « no pending request », et le ticket du worker reste working dans le live-state.
---
# Finding — tests MCP T7 (2026-06-24, QA)
## Contexte
Série de tests du rendez-vous `idea_ask_agent ⇄ idea_reply` et du backstop no-reply (cap ~600s), demandés par Main :
- **T7d** (debug build x3, avant-plan) : cumul ~3m49s < 600s reply OK.
- **T7b** (9 étapes `sleep 82`, entrelacées) : le harness BLOQUE tout `sleep` avant-plan exécutées en run_in_background ; reply OK (tardif mais < fenêtre côté ticket T7b).
- **T7e** (release build répété, avant-plan, sans sleep ni bg) : 7 cycles, cumul réel **08:01:14 → 08:14:21 = 13 min 07 s**, franchit largement 600s.
## Défaut observé
À la fin de T7e (et de T7b, terminé au même tour, après >600s d'occupation continue), les DEUX `idea_reply` (tickets `4613262f…` T7b et `d2398b22…` T7e) sont refusés :
```
invalid input: no pending request to reply to for agent aefdbd61-…
```
→ Le backstop a libéré le **demandeur** (Main) côté rendez-vous après dépassement du cap, ce qui est le comportement voulu. MAIS :
1. Le **résultat réel** (builds OK, horodatages non falsifiés) ne peut **plus être délivré** : il est perdu pour le demandeur.
2. `idea_workstate_read` montre encore le worker QA `status=working ticket=d2398b22…` **après** le rejet du reply → le live-state du **worker** n'est PAS réconcilié quand le backstop ferme le rendez-vous côté demandeur. Incohérence demandeur (libéré) vs worker (toujours working).
## Conséquence / piste
- Le cap 600s du rendez-vous reste trop court pour des tâches lourdes légitimes (cf. [[rendezvous-600s-cap-too-short-heavy-tasks]]) : ici un travail réel et honnête >13 min est « jeté ».
- À corriger : quand le backstop ferme un rendez-vous, soit (a) accepter encore un reply tardif (le router vers le demandeur même après timeout / le persister), soit (b) réconcilier le live-state du worker (passer son ticket à done/abandon) pour ne pas laisser un `working` fantôme. Voir [[backstop-fires-on-intra-task-turn-rootcause]] et [[rendezvous-no-reply-backstop-design]].
## Reproductible
Charge avant-plan réelle (`cargo build --release --workspace` en boucle clean) dépassant 600s, puis tenter `idea_reply` sur le ticket d'origine → rejet systématique.

View File

@ -0,0 +1,35 @@
---
name: opencode-llamacpp-replaces-ollama
description: memory note opencode-llamacpp-replaces-ollama
metadata:
type: project
---
---
title: OpenCode local provider — llama.cpp remplace Ollama (chantier livré)
type: decision
description: Le provider local d'OpenCode passe d'Ollama à llama.cpp (llama-server OpenAI-compatible). Profil éditable baseURL/apiKey/model. Livré sur develop (merge 2e98f1f). Clôt le diagnostic tool-calling Ollama.
---
# OpenCode local : llama.cpp remplace Ollama (2026-07-11)
## Pourquoi
Le tool-calling des modèles locaux via **Ollama** ne s'est jamais déclenché nativement en live : OpenCode fuyait les appels d'outils en texte `<function=...>` (émulation par prompt), jamais parsés. Diagnostic utilisateur : la faute est dans la relation **OpenCode↔Ollama**. Décision : abandonner Ollama comme provider OpenCode et passer à **llama.cpp** (`llama-server`, endpoint OpenAI-compatible). Voir historique : [[opencode-config-model-agnostic-tool-diagnostic]], [[opencode-ollama-native-tool-call-flag]], [[opencode-ollama-tool-calling-surface-overload]] (contexte Ollama, désormais superseded).
## Contrat livré
`OpenCodeConfig` (domaine + DTO app-tauri + type frontend) :
`{ baseURL: String (requis, http/https), apiKey: Option<String>, model: String (requis), reasoning: Option<bool>=true, attachment: Option<bool>=false }`. Sérialisation wire camelCase (`baseURL` via serde rename), `apiKey`/`reasoning`/`attachment` `skip_serializing_if=None`.
Seed profil canonique : `opencode-ollama`**`opencode-llamacpp`** ("OpenCode + llama.cpp"), command `opencode`, adapter `OpenCode`, defaults `baseURL=http://localhost:8080/v1`, `apiKey=sk-no-key`, `model=qwen3-coder-30b`.
`opencode_config_json` (crates/application/src/agent/lifecycle.rs) régénère un opencode.json MINIMAL : provider `llamacpp` (`npm=@ai-sdk/openai-compatible`, `options.baseURL/apiKey` du profil, def modèle `llamacpp/<model>` avec `tool_call:true,reasoning,attachment`), MCP `idea` inchangé, `permission bash/edit=ask`, `disabled_providers=["anthropic","openai","gemini","ollama"]`. `apiKey` omis (pas `null`) si absent. Plus aucun résidu `ollama`/préfixe `ollama/`/allow-list diagnostic.
Frontend : wizard profil OpenCode = 3 champs éditables **Base URL / Model / API key(optionnel)** (`FirstRunWizard.tsx`, `profile.ts`, mocks). L'embedder Ollama (`ollamaDetected`, mémoire vectorielle) est un AUTRE usage, NON touché.
## État
- Fichiers : domain/profile.rs, application/{catalogue.rs,lifecycle.rs,tests/agent_lifecycle.rs}, infrastructure/session/opencode.rs ; frontend domain/index.ts, adapters/mock/*, features/first-run/*.
- QA VERT : domain 244, application 81+64, infra 263/10 (les 10 = bind-port sandbox, identiques sur develop = non-régression), frontend 574, tsc propre. Claude/Codex non régressés.
- Git : commit `4e70631`, merge `--no-ff` `2e98f1f` sur `develop`. Base branche = `eaba05d` (profil OpenCode process-backed). WIP diagnostic Ollama capsulé dans `3cdedf2` sur `feature/opencode-glm47-flash-tool-diagnostic`. PAS de push distant.
- Tickets : #32 fermé ; #42/#43 (Ollama) retirés du store.
## Reste ouvert
E2E live llama.cpp jamais exécuté (aucun `llama-server` joignable sur :8080 au moment QA ; `llama-server` 9859 + `opencode` 1.17.18 installés). À valider côté utilisateur : lancer llama-server, relancer l'Appointe rebuild, créer un agent profil llama.cpp, vérifier un vrai `tool_use` MCP (pas de `<function=` texte). Rebuild AppImage requis pour le live (binaire qui tourne = AppImage). Build via `NO_STRIP=true` cf [[appimage-build-no-strip-relr-dyn-fix]].

View File

@ -1,25 +0,0 @@
---
name: reconcile-live-state-implemented-feature-branch
description: memory note reconcile-live-state-implemented-feature-branch
metadata:
type: project
---
---
title: Réconciliation du live-state au reboot — implémentée (feature/reconcile-live-state)
type: project
description: Le chantier ReconcileLiveState (3 lots) est implémenté et vert (1623 tests), non committé ; détail des fichiers et de l'arbitrage project_id/provider.
---
Implémenté le 2026-06-23 sur `feature/reconcile-live-state` (ticket Main a7fcb5e4, dont le rendez-vous `idea_reply` a expiré pendant le build — résultat consigné ici). **Non committé** (Git tranche). Cadrage : [[live-state-reboot-reconciliation-design]].
**Résultats réels** : `cargo test --workspace` = **1623 passed, 0 failed**. `cargo clippy --workspace --all-targets` = 0 erreur, aucun warning sur le code ajouté (warnings restants tous pré-existants).
**Lot 1 (domaine, `crates/domain/src/live_state.rs`)** : `LiveState::reconcile_orphans(is_live, now_ms) -> Vec<LiveEntry>` pure (ne mute pas self). Orphelin = status ∈ {Working,Waiting,Blocked} ET !is_live → réécrit status=Idle, intent gardé, ticket=None, last_delegation=None, progress=Some(`STALE_AT_RESTART_MARKER`), updated_at_ms=now. Const pub `STALE_AT_RESTART_MARKER = "(stale — session not running at restart)"`. +4 tests.
**Lot 2 (application, nouveau `crates/application/src/workstate/reconcile.rs`)** : use case `ReconcileLiveState` sur `LiveStateStore`+`LiveAgentRegistry`(ISP)+`Clock` ; `execute(ReconcileLiveStateInput{project_id})` charge → reconcile_orphans → upsert par ligne. +3 tests. Exporté via workstate/mod.rs + application/lib.rs.
**Lot 3 (app-tauri, `state.rs`+`commands.rs`)** : provider par-root `AppReconcileLiveState` (résout root via `ProjectStore::load_project`, `FsLiveStateStore::new(&root)`, registre = `LiveSessions` existant). Champ `reconcile_live_state` sur AppState. Hook best-effort dans `open_project` après `reconcile_layouts`. **Acte système** : écrit le port `LiveStateStore` directement, jamais via `OrchestratorCommand::SetWorkState` → self-only de `idea_workstate_set` préservé par construction.
**Arbitrage à valider QA/Architect** : la résolution project_id→root se fait au composition root (le store est root-bound), donc `ReconcileLiveState::execute` reçoit bien `ReconcileLiveStateInput{project_id}` (conforme Lot 2 + hook Lot 3 à la lettre) mais ne relit pas project_id dans le flux pur (`_input`). Variante sans ce param redondant = ajustement trivial si souhaité.
**Fichiers (6)** : domain/src/live_state.rs ; application/src/workstate/reconcile.rs (nouveau) ; application/src/workstate/mod.rs ; application/src/lib.rs ; app-tauri/src/state.rs ; app-tauri/src/commands.rs.

View File

@ -1,39 +0,0 @@
---
name: resume-after-appimage-rebuild-mcp-e2e
description: memory note resume-after-appimage-rebuild-mcp-e2e
metadata:
type: project
---
---
name: resume-after-appimage-rebuild-mcp-e2e
description: Point de reprise — programme live-state CLOS+RELEASÉ (0.3.0), discordance .gitignore D19-4 TRANCHÉE. Prochain chantier = validation MCP e2e UX. Chantiers restants priorisés.
metadata:
type: project
---
# Reprise — état au 2026-06-22 (run a6ced819)
## Où on en est
- **Programme live-state / persistance CLOS et RELEASÉ** : `develop` @ `36be0cb` (LS8), `main` @ `29232dd` = `release(0.3.0): intègre develop dans main`. Détail : [[live-state-persistence-program-closed-ls8]]. Doc : `docs/LS8-live-state-persistence-closure.md` + `ARCHITECTURE.md` §21.
- **AppImage rebuildée + tournante** : `IdeA_0.3.0_amd64.AppImage` (le nom n'est plus 0.1.0). Le binaire live embarque tout le programme ; 6 ponts MCP connectés/ESTAB observés → point 1 du chantier e2e (agent voit ses outils MCP sans action manuelle) déjà satisfait en réel. Recette rebuild (rappel) : `npm --prefix frontend run build` puis depuis `crates/app-tauri/` `APPIMAGE_EXTRACT_AND_RUN=1 NO_STRIP=1 ../../frontend/node_modules/.bin/tauri build --bundles appimage` ; remplacer l'AppImage installée → relancer.
- **Discordance .gitignore ↔ D19-4 : TRANCHÉE** (par l'utilisateur) et exécutée par Git → commit `47b3806` sur `develop` (`chore(gitignore): désuivre .ideai/conversations/`). Périmètre réel : seul `.ideai/conversations/` était discordant (16 fichiers suivis, 8 handoff.md + 8 log.jsonl) ; `.ideai/run/` et `.ideai/live-state.json` étaient déjà ignorés/propres. Désuivis (rm --cached, conservés sur disque) + règle ajoutée `.gitignore:58`. Plus une décision en attente.
## Prochain chantier (priorité 1) : VALIDATION MCP END-TO-END UX
Objectif = valider/durcir en situation RÉELLE le parcours inter-agents (le code est probablement bon, Bugs 1→7 corrigés cf. [[mcp-bridge-and-delegation-runtime-notes]], mais ces bugs ne sont apparus qu'en live). À vérifier sur l'AppImage 0.3.0 :
1. Un agent lancé par IdeA voit ses outils MCP sans action manuelle. (déjà OK observé)
2. Délégation vers agent FROID et vers agent BACKGROUND (node_id None) : tâche bien écrite dans le PTY, pas de 1er tour perdu, pas de `Busy` fantôme après interruption/annulation (Bugs 3/4/6/7).
3. Un `ask` sans réponse ne wedge pas la connexion du demandeur (Bug 5).
4. Profils Claude/Codex utilisent MCP par défaut ; fallback fichier+prose cohérent sinon.
5. Observabilité UI des délégations/replies (aujourd'hui opaque) — sujet UX.
**CONTRAINTE MÉTHODO** : NE PAS réparer/valider le système inter-agents VIA le système inter-agents (`idea_ask_agent`). Piloter via les **subagents natifs (outil Agent)**, et demander à l'utilisateur de faire tourner l'AppImage + observer (Main ne peut pas valider seul depuis l'intérieur). Diag live : `ss -xp | grep idea-mcp` (ponts connectés) ; `~/.claude/projects/<encoded-run-dir>/*.jsonl` (transcript ; pas de nouveau fichier = tâche jamais soumise).
## Autres chantiers restants (depuis [[remaining-work-idea-agent-control-ide]])
- **Auto-update mémoire/contexte EN COURS de session** (aujourd'hui injection au lancement seulement) — priorité 1.
- **Multi-fenêtres / déplacement d'onglet** du registre de sessions — priorité 2 (UI).
- Optionnels différés : activation réelle du seam LLM de handoff (contrat figé ADR LS5, défaut heuristique) ; sweep périodique de rotation (idempotent, non câblé).
## Friction outillage notée (par Git, sans incidence résultat)
Le `settings.local.json` du run dir a un `deny` qui bloque Edit/Write et certaines redirections Bash pour les agents en run dir isolé ; Git a dû éditer `.gitignore` via `tee -a`. À assouplir si on veut des éditions de fichiers propres côté run dir.
## Contrainte d'écriture (rappel)
Main + agents en run-dir isolé ne peuvent PAS écrire l'arbre du project root (docs/, ARCHITECTURE.md, .ideai/memory/*.md fichiers) — permissions déniées. Mémoire IdeA : outil `idea_memory_write` (create/replace par slug ; pas de delete). Suppression fichiers mémoire / édition MEMORY.md / CLAUDE.md / .gitignore du project root : passer par **Git**.

View File

@ -0,0 +1,42 @@
---
name: sandbox-eperm-bind-false-green-web-server
description: memory note sandbox-eperm-bind-false-green-web-server
metadata:
type: project
---
---
name: sandbox-eperm-bind-false-green-web-server
description: Les sandboxes de DevBackend et QA bloquent TcpListener::bind (EPERM) ; tout `cargo test` sur web-server/app-tauri y rend un VERT QUI NE PROUVE RIEN. Vérifier hors sandbox.
metadata:
type: reference
---
# Faux vert : `TcpListener::bind` interdit dans les sandboxes agents
## Le fait
Les environnements d'exécution de **DevBackend et de QA** refusent `TcpListener::bind("127.0.0.1:0")` avec `Operation not permitted (os error 1)`. Tout test qui ouvre un socket y est structurellement inexécutable.
Crates concernés : `web-server`, `app-tauri` (pont MCP, serveur embarqué).
## Pourquoi c'est dangereux, pas juste gênant
Le 2026-07-16, ce piège s'est refermé **deux fois dans la même journée** :
1. **#68 B1** — DevBackend annonce « 57 passed ». En réalité le test `run_embedded_stop_shuts_down_accept_loop` sortait en silence sur EPERM via un repli, et le test du core partagé basculait sur un dispatch in-process. Rejoué **hors sandbox** : échec réel, révélant un **vrai bug produit**`run_embedded` ne réconciliait pas le port effectif avec la config, donc `origin_allowed` comparait l'origine à `http://127.0.0.1:0` et un serveur embarqué sur port éphémère **rejetait toutes les requêtes API en 403**. Le trou n'avait jamais été vu parce que rien ne consommait `run_embedded`.
2. **#72** — même schéma, cette fois signalé honnêtement par DevBackend (« 63 passed; 2 failed » sur EPERM) après consigne explicite.
Un repli silencieux sur EPERM transforme un test en décoration : il passe sans rien exercer, et masque la classe de bug qu'il était censé attraper.
## La règle
- **Aucun repli EPERM silencieux.** Un test qui ne peut pas s'exécuter doit **échouer visiblement** ou être `#[ignore]` explicite avec sa raison — jamais « passer ».
- **Tout vert sur `web-server`/`app-tauri` doit être produit hors sandbox** (`dangerouslyDisableSandbox: true` côté Main, qui n'a pas la restriction). Ne jamais accepter un vert sandboxé comme preuve sur ces crates.
- DevBackend et QA doivent **dire ce qu'ils ont pu exécuter et ce qu'ils n'ont pas pu**, plutôt que de rendre un chiffre global.
- Les `#[ignore = "requires local socket bind permission"]` légitimes (ex. `mcp_bridge::tests::end_to_end_over_real_loopback`) se vérifient en les **lançant** hors sandbox avec `--ignored`, pas en raisonnant dessus.
## Principe général
Ne pas confondre « les tests sont verts » et « le code marche ». Le vert d'un environnement contraint ne dit rien du produit. Cette leçon vaut au-delà du bind : un environnement qui ne peut pas exercer un chemin ne peut pas le valider.
Lien : [[appimage-build-no-strip-relr-dyn-fix]] (autre piège d'environnement de cette machine),
[[rendezvous-600s-cap-too-short-heavy-tasks]].

View File

@ -0,0 +1,23 @@
---
name: ticket13-f0-frontend-transport-inventory
description: Résultat de l'inventaire B0/F0 frontend du chantier client/serveur #13 — la frontière gateways transport-neutres existe déjà, F1 = ajout d'adapters HTTP+WS.
metadata:
type: reference
---
Inventaire B0/F0 (ticket #13 server/client mode), côté frontend TS/React. Doc : `docs/ticket13-f0-frontend-transport-inventory.md`.
**Constat clé** : la frontière « gateways TS transport-neutres » du plan est DÉJÀ réalisée et testée.
- `src/ports/index.ts` = 21 gateways sans Tauri ; toute la couche `features/`+`app/` en dépend via DI (`useGateways()`).
- `src/adapters/*` = seul lieu important `@tauri-apps/api`.
- Garde L1 `src/app/no-direct-invoke.test.ts` casse la CI si un fichier hors `adapters` importe Tauri / appelle `invoke(`.
- `src/app/di.tsx` `resolveGateways()` bifurque déjà Tauri vs mock → F1 = ajouter `createHttpWsGateways()` (3ᵉ impl).
**Donc F1 = écrire un 2ᵉ jeu d'adapters derrière des ports inchangés, aucun composant métier à réécrire.**
**Flux Channel (→ WebSocket)** : (1) `terminal.ts` PTY, (2) `agent.ts` PTY agent (réutilise `makeTerminalHandle`), (3) `ticket.ts` `sendTicketChat` `Channel<ReplyChunk>`. Tous modélisés côté port par callback `onData`/`onChunk`. Le contrat PTY WS du carnet mappe 1-pour-1 sur `TerminalHandle` (write/resize/detach/close, detach≠close, scrollback au reattach) → port inchangé pour F3. `TerminalView.tsx` ne connaît que le port.
**Event portable** : `system.onDomainEvent("domain://event")` (bus domaine) → WS serveur→client.
**Desktop-only à cadrer** : `system.pickFolder` (dossier = machine serveur ≠ client web → file-picker serveur), `window.ts` (WebviewWindow OS) et `focusedProject.ts` (multi-fenêtres OS) probablement hors V1 web. `uiPreferences` (localStorage) marche tel quel.
Convention DTO à préserver côté adapter HTTP : commandes snake_case, payloads camelCase souvent enveloppés `{ request: {...} }`.

View File

@ -0,0 +1,17 @@
---
name: ticket13-f1-http-ws-adapter-delivered
description: Le 3e jeu d'adapters frontend (HTTP request/response + squelette WebSocket) du chantier client/serveur #13 est livré et vert, derrière les ports inchangés.
metadata:
type: reference
---
Ticket #13 lot F1 livré sur feature/ticket13-server-client-mode. Nouveau dossier `frontend/src/adapters/http/` = 3e implémentation des gateways, à côté de Tauri (desktop) et mock.
**Fichiers** : `httpInvoker.ts` (POST /api/invoke {command,args}, ErrorDto→GatewayError, fetch injectable), `frames.ts` (contrat frames WS B0 + base64), `wsLiveClient.ts` (squelette WS multiplexé : corrélation id↔replyTo, routage terminal.output par session, dispatch event.domain), `requestResponseGateways.ts` (13 gateways R/R, commandes/enveloppes IDENTIQUES aux adapters Tauri), `streamGateways.ts` (HttpSystem/Agent/Ticket/Terminal + makeWsTerminalHandle detach≠close), `unsupported.ts` (Web{Window,FocusedProject,Remote}Gateway, code UNSUPPORTED_ON_WEB), `index.ts` (createHttpWsGateways(config?), endpoints via window.location). Tests : httpInvoker.test.ts, wsLiveClient.test.ts.
**Seam DI** : `app/di.tsx``resolveTransport()` 3-way (mock > http > tauri), web sélectionné par `VITE_TRANSPORT="http"`. Tauri reste défaut (desktop inchangé).
**Décision de contrat clé (à confirmer DevBackend)** : transport RPC générique `POST /api/invoke {command,args}` choisi PLUTÔT que l'arbre REST du brouillon B0 — préserve 1:1 tous les DTO Tauri, zéro divergence, bascule REST future ne touche que httpInvoker.ts. Autres points à trancher : placement token WS (pas en URL ; navigateur ne peut pas fixer d'en-tête upgrade), forme réponse terminal.open/agent.launch (ack terminal.attached avec session.sessionId), projectId absent du port openTerminal.
**Reporté F3/B5/B6** (TODO(F3/B5)) : round-trip xterm réel, reconnexion/backpressure, replay seq/gap, sink chat par-session pour sendTicketChat, agents structurés. Pas de serveur avant B3/B4.
État : build vert (tsc+vite), garde no-direct-invoke verte, 77 fichiers/724 tests verts. Voir [[ticket13-f0-frontend-transport-inventory]].

View File

@ -0,0 +1,19 @@
---
name: ticket13-f2-web-readonly-client-delivered
description: Le client web read-only (pairing cookie, liste projets, ouverture read-only + snapshot work-state) du chantier client/serveur #13 est livré et vert.
metadata:
type: reference
---
Ticket #13 lot F2 livré sur feature/ticket13-server-client-mode. Clôture frontend du premier incrément livrable (B0→B4/F2).
**Nouveau** : `adapters/http/webSession.ts` (WebSession : flag localStorage « paired » — PAS le cookie HttpOnly ; `pair(code)`→POST /api/pair ; `notifyUnauthorized()` clear+notify ; singleton `getWebSession()`), `features/web/` (PairingScreen, WebWorkspace read-only, WebApp gate). Tests : webSession.test.ts, WebApp.test.tsx.
**Modifié** : httpInvoker (credentials same-origin + callback onUnauthorized sur 401), http/index.ts (câble onUnauthorized→webSession), app/main.tsx (monte <WebApp/> si resolveTransport()==="http", desktop inchangé).
**Flux** : non-paired→PairingScreen ; pair OK (cookie HttpOnly posé serveur)→WebWorkspace ; 401 d'un /api/invoke→retour pairing ; « se déconnecter »=clear flag local (révocation serveur=B8). Cookie jamais lisible en JS (HttpOnly) : on se fie au 200 + flag de routage.
**Read-only** : WebWorkspace n'appelle QUE list_projects, open_project, get_project_work_state (via gateways DI). N'appelle PAS onDomainEvent/health/firstRunState → aucune commande hors-allowlist, pas de WS (live update = F3/B5, snapshot ponctuel pour l'instant).
**À confirmer B4** : get_project_work_state dans l'allowlist ; open_project sans effet de bord dangereux en read-only ; 401 (pas 403) sur cookie manquant ; code HTTP mauvais code /api/pair (401/403 → « Code d'appairage invalide »).
Build vert, garde no-direct-invoke verte, 79 fichiers/736 tests verts, desktop inchangé. Suite de [[ticket13-f1-http-ws-adapter-delivered]] ; inventaire [[ticket13-f0-frontend-transport-inventory]].

View File

@ -0,0 +1,19 @@
---
name: ticket13-f3-xterm-websocket-delivered
description: L'adapter WS terminal (round-trip xterm, reconnexion+replay, états connexion) du chantier client/serveur #13 est finalisé et vert côté frontend.
metadata:
type: reference
---
Ticket #13 lot F3 livré sur feature/ticket13-pty-websocket. Finalise l'adapter WS terminal depuis le squelette F1. Travail 100% dans frontend/src/adapters/http/ ; port TerminalGateway/TerminalHandle et TerminalView INCHANGÉS.
**wsLiveClient.ts** : machine d'état connexion (connecting/connected/reconnecting/closed, getConnectionState()+onConnectionStateChange), reconnexion auto (backoff, setTimeout injectable) → re-attach_terminal avec lastSeq + repaint scrollback borné + notices « déconnecté »/« reconnecté » écrites dans xterm via le sink. Suivi des sessions terminales (map `terminals`) pour le replay. Routage terminal.status exited → notice + onStatus + untrack. openTerminal/attachTerminal/detachTerminal/closeTerminalSession haut-niveau. API bas-niveau F1 (setOutputSink/send/domain events) conservée pour agent/system gateways.
**streamGateways.ts** : HttpTerminalGateway délègue aux nouvelles méthodes ; makeWsTerminalHandle.detach→detachTerminal (untrack, PTY vivant), close→closeTerminalSession (tue).
**frames.ts** : AttachedPayload.status? + StatusPayload.
**Contrat B5 (server.rs) confirmé** : frames terminal.open{cwd,rows,cols} (pas de projectId)/attach{sessionId,rows,cols,lastSeq}/input{sessionId,bytesBase64}/resize/detach/close/ping ↔ terminal.attached{session,scrollback:[{seq,bytesBase64}],nextSeq,status,gap,assignedConversationId}/output{sessionId,seq,bytesBase64}/status{sessionId,status,exitCode}/error/pong. Ack unifié terminal.attached. Scrollback = 1 entrée seq:0 (tous octets) ou vide.
**Écarts B5 à arbitrer** : (1) pas de replay delta — le serveur rejoue TOUT le scrollback, lastSeq ne sert qu'au flag gap ⇒ duplication possible à la reconnexion (conforme « V1 scrollback borné »). (2) multi-onglets « dernier gagne » SILENCIEUX — l'attachement évincé ne reçoit aucune frame (pas de crash, mais pas d'indication). (3) indication d'état = notices dans xterm (port inchangé).
**Tests** : terminalGateway.test.ts (round-trip), wsLiveClientReconnect.test.ts (états/reconnexion/exited). Build vert, garde no-direct-invoke verte, 81 fichiers/743 tests verts, desktop inchangé.
**Bloqueur run live** (dette carnet, hors F3) : `idea --serve` ne sert pas les assets web same-origin ⇒ app web pas lançable en navigateur tant que le lot « servir dist/ » n'est pas fait. Suite de [[ticket13-f1-http-ws-adapter-delivered]], [[ticket13-f2-web-readonly-client-delivered]].

View File

@ -0,0 +1,19 @@
---
name: ticket13-f4-web-agent-surface-delivered
description: L'agent CLI web (agent.launch → terminal.attached, réattache sans relance, cellule agent réutilisant TerminalView) du chantier client/serveur #13 est livré et vert côté frontend.
metadata:
type: reference
---
Ticket #13 lot F4 livré sur feature/ticket13-pty-websocket. Rend la surface agent fonctionnelle en mode web via les gateways DI ; CLI serveur, web = affichage.
**Adapter** : wsLiveClient.launchAgent(params) réutilise attachInternal de F3 → frame agent.launch, ack unifié terminal.attached, session trackée dans la map `terminals` (reconnexion/replay/exited comme un terminal), renvoie {sessionId, scrollback, assignedConversationId, status}. HttpAgentGateway.launchAgent → ws.launchAgent (pose assignedConversationId sur le handle) ; HttpAgentGateway.reattach → ws.attachTerminal (frame terminal.attach, PAS de relance). Chemin bas-niveau F1 dupliqué supprimé.
**UI (câblage, pas de nouveau composant terminal)** : features/web/WebAgentCell.tsx réutilise TerminalView (agentMode) avec agent gateway DI comme open=launchAgent/reattach=reattach, persiste sessionId. WebWorkspace : affordance « Ouvrir » par agent du snapshot work-state → monte WebAgentCell.
**Contrat B6 (server.rs) confirmé, aucun écart** : agent.launch payload plat camelCase {projectId,agentId,nodeId,rows,cols,conversationId} → ack terminal.attached{assignedConversationId}. Agent structuré → erreur UNSUPPORTED (canal PTY-only) surfacée par la bannière TerminalView. Réattache = terminal.attach (no respawn). Singleton guard AGENT_ALREADY_RUNNING déjà géré par TerminalView.
**Points UI à signaler** : (1) l'affordance liste les agents du snapshot work-state ; lister tous les agents exigerait list_agents sur l'allowlist B4. (2) write-portal (injection délégation) NON câblé en web V1 (affichage + frappe seulement). (3) WebAgentCell ne gère pas de nœud layout.
**Tests** : adapters/http/agentGateway.test.ts (launch/assignedConversationId/reattach/input/output/resize/UNSUPPORTED/NOT_FOUND), WebApp.test.tsx (+ouverture cellule). Build vert, garde no-direct-invoke verte, 82 fichiers/749 tests verts, desktop inchangé.
**Bloqueur run live** (dette carnet, hors F4) : `idea --serve` ne sert pas les assets web same-origin ⇒ round-trip navigateur pas validable tant que le lot « servir dist/ » n'est pas fait. Suite de [[ticket13-f3-xterm-websocket-delivered]], [[ticket13-f1-http-ws-adapter-delivered]], [[ticket13-f2-web-readonly-client-delivered]].

View File

@ -0,0 +1,17 @@
---
name: ticket13-f5-web-live-surfaces-delivered
description: Les surfaces live web (workstate live via event.domain, background tasks cancel/retry, inbox, re-synchro au reconnect) du chantier client/serveur #13 sont livrées et vertes.
metadata:
type: reference
---
Ticket #13 lot F5 livré sur feature/ticket13-pty-websocket. Rend les surfaces live fonctionnelles en mode web.
**Réutilisation clé** : le hook desktop transport-neutre `features/workstate/useProjectWorkState` (refresh du read-model sur event.domain) est réutilisé TEL QUEL — c'est lui qui donne la parité live. PAS de réutilisation de ProjectWorkStatePanel (dépend de useLayout + attach/stop-vers-cellule, spécifiques au grid desktop, hors read-only). WebWorkspace rend une vue lean : agents live/idle/busy, background tasks (Cancel/Retry via workState gateway), inbox par agent, + cellule agent F4.
**Adapter** : wsLiveClient.needsReconnect() = terminals.size>0 OU domainEventHandler!=null → la reconnexion marche aussi pour un abonnement live sans terminal (le handler domaine persiste à travers la reconnexion). Nouveau webLive.ts = singleton getWebLiveClient()/setWebLiveClient() (createHttpWsGateways enregistre le ws) exposant l'état de connexion au feature web SANS le mettre dans le port SystemGateway. Nouveau hook features/web/useLiveReconnect.ts : re-refresh du read-model sur transition reconnecting→connected (events manqués pendant coupure ; récupération = re-fetch complet du snapshot, pas de replay serveur).
**À confirmer B7** : (1) cancel_background_task/retry_background_task sur l'allowlist web write. (2) inbox surfacée via le read-model workstate, PAS via un flux notifications distinct (si B7 en a un, non câblé). (3) re-synchro = re-fetch complet (workstate = snapshot complet), pas de delta par event.
**Tests** : WebWorkspaceLive.test.tsx (event.domain→refresh, background render+cancel, reconnect re-sync), wsLiveClientReconnect (reconnexion pour abonnement domaine). Build vert, garde no-direct-invoke verte, 83 fichiers/753 tests verts, desktop inchangé.
**Bloqueur run live** (dette carnet, hors F5) : `idea --serve` ne sert pas les assets web same-origin. Suite de [[ticket13-f4-web-agent-surface-delivered]], [[ticket13-f3-xterm-websocket-delivered]], [[ticket13-f2-web-readonly-client-delivered]].

View File

@ -0,0 +1,64 @@
---
name: ticket14-local-lan-openai-adapter-scoping
description: memory note ticket14-local-lan-openai-adapter-scoping
metadata:
type: project
---
# Ticket #14 — cadrage figé : adapter IA local/LAN OpenAI-compatible canonique
Périmètre figé (review agent-user faite) : canal = **adapter HTTP natif** parlant une API compatible OpenAI/Ollama ; usage **transparent** vs Claude/Codex (mêmes cellules, conversation headless canonique, historique, reprise, délégation, vivacité) ; **PUREMENT ADDITIF** (zéro régression Claude Code / Codex CLI) ; **parité tool-calling/MCP** dès ce ticket avec **dégradation propre** si le modèle n'expose pas les outils.
## 0. Asymétrie structurelle (commande TOUT le design)
| | Claude/Codex (existant) | Modèle local HTTP (#14) |
|---|---|---|
| Nature | binaire CLI spawné | serveur HTTP appelé |
| Boucle agentique | conduite par la CLI | conduite par **IdeA** |
| Rôle MCP d'IdeA | serveur ; CLI = client | pas de client ⇒ IdeA = orchestrateur d'outils |
| Contexte .md | la CLI lit son convention file | injecté en **system message** par l'adapter |
| Conversation | id moteur (session_id/thread_id) | API **stateless** ⇒ transcript possédé par IdeA |
Conséquence : le nouvel adapter **n'utilise PAS** `session/process.rs` (run_turn/spawn/drain). Il ne partage avec claude.rs/codex.rs que les **types de port** (`AgentSession`, `ReplyEvent`). Cette absence de code partagé mutable EST la garantie d'isolation.
## 1. Cartographie hexagonale
**Domaine (crates/domain)**
- Variante `StructuredAdapter::OpenAiCompatible` (profile.rs:244). `provider_key()``"openai-compatible"` (contrat persistance providers.json). Sérialise camelCase `"openAiCompatible"`.
- Nouveau VO validé (parse-don't-validate) `HttpChatConfig { endpoint:String (http/https non vide), model:String (non vide), api_key_env:Option<String> (nom de var env valide, JAMAIS la clé), request_timeout_ms:Option<u32>, connect_timeout_ms:Option<u32>, max_tool_iterations:Option<u16> (garde-fou boucle, défaut ~16) }`. Le domaine ne résout jamais la clé (pas d'I/O env), il porte le **nom** de variable.
- Rattachement `AgentProfile.chat_http: Option<HttpChatConfig>` avec `#[serde(default, skip_serializing_if="Option::is_none")]` (miroir exact de `mcp`/`liveness`/`rate_limit_pattern`, profile.rs:570-604) ⇒ **zéro régression de sérialisation** (profils Claude/Codex bit-identiques). Test round-trip obligatoire (miroir profile_without_mcp_round_trips_identically).
**Nouveau port tool-calling (le seam clé — le pont MCP .mcp.json/config.toml + bridge `idea mcp-server` NE s'applique pas, pas de client CLI)**
```rust
pub struct ToolSpec { name:String, description:String, input_schema:serde_json::Value }
#[async_trait] pub trait ToolInvoker: Send+Sync {
fn tools(&self) -> Vec<ToolSpec>;
async fn call(&self, name:&str, args_json:&str) -> Result<String, ToolInvocationError>;
}
```
Implémenté en **app-tauri** en déléguant à la **même** `OrchestratorService::dispatch` (orchestrator/service.rs:1082) que le endpoint MCP ⇒ parité délégation/rendez-vous (idea_ask_agent/idea_reply), gating permissions/sandbox conservé. Injecté dans la factory via `with_tool_invoker(Arc<dyn ToolInvoker>)` (jumeau de `with_sandbox_enforcer`). `None` ⇒ tool-calling désactivé proprement (chat nu).
**Infra** : nouveau fichier `crates/infrastructure/src/session/openai_compat.rs` (jumeau structurel de codex.rs SANS process). Client HTTP **reqwest + rustls-tls** (jamais native-tls — spike AppImage §13-2, confiné au crate infra). Parsing isolé pur `parse_chat_delta`/`parse_completion` (miroir de parse_event) — seul endroit qui connaît le schéma OpenAI (choices[].message, tool_calls[], SSE data:).
**Routing — seul contact avec l'existant** : `StructuredSessionFactory::start` (factory.rs:111) = **UN bras de match ajouté** ; bras Claude/Codex INTOUCHÉS ; `supports()` reste `profile.is_selectable()` (=structured_adapter.is_some(), profile.rs:857) ⇒ sélectionnable sans changer le prédicat.
## 2. Contrat de session headless
`send(prompt)` : 1) transcript.push(user) ; 2) émettre Heartbeat ; 3) POST {endpoint}/chat/completions {model, messages:transcript, tools?:invoker.tools(), stream:true} ; 4) si tool_calls ⇒ pour chaque: ToolActivity{label=name} + result=invoker.call(name,args) + push(assistant tool_call)+push(tool result), reboucler (borné max_tool_iterations) ; 5) sinon streamer chunks ⇒ TextDelta (+tap→Announcement, cf claude.rs:337) ; 6) réponse complète ⇒ push(assistant) + **Final{content}** ; 7) persister transcript run dir. Contrat de flux universel respecté (un seul Final terminal) ⇒ send_blocking/drain_with_readiness marchent tels quels.
**Erreurs (jamais de corps HTTP brut propagé)** : endpoint injoignable 1er contact/probe ⇒ `AgentSessionError::Start` (UI erreur, IdeA non bloqué) ; réseau/coupure en tour ⇒ `Io` ; JSON illisible/schéma ⇒ `Decode` (diagnostic court) ; timeout ⇒ `Timeout` via send_blocking, session non tuée ; modèle absent (404/400) ⇒ `Start` dédié.
**Reprise sans mélange d'ids (invariant du ticket)** : /chat/completions **stateless** ⇒ AUCUN id provider ⇒ `conversation_id()` retourne **None** (rien à mélanger). État = transcript provider-shaped (roles+tool_calls) **possédé par IdeA**, persisté dans le **run dir stable** `.ideai/run/<agent-id>/chat-transcript.json` (clé = agent, jamais id provider). Reprise = ré-instanciation factory ⇒ rechargement transcript ; `seed_conversation_id` (factory.rs:63) **ignoré** par cet adapter (documenter). DISTINCT du conversation-log LS6 (log.jsonl, dérivé ReplyEvent, surface humaine, inchangé) — deux artefacts, aucun mélange.
**Contexte** : `start` reçoit `ctx:&PreparedContext` ; `ctx.content` (ports.rs:120) = Markdown rendu ⇒ injecté comme premier message **system** (le serveur HTTP ne lit pas de convention file).
## 3. Seam tool-calling / dégradation
Parité : ToolInvoker câblé ⇒ modèle local voit idea_* via la même OrchestratorService (délégation/rendez-vous/contexte/mémoire/tickets identiques, gating conservé). Dégradation 3 niveaux : (1) ToolInvoker None ⇒ chat nu ; (2) profil opte sans outils ⇒ idem ; (3) endpoint rejette param `tools` ⇒ détecter, retirer tools, rejouer chat nu, 1 diagnostic — l'agent converse toujours, sans déléguer. Garde-fou max_tool_iterations (modèles locaux moins fiables) ⇒ au plafond, Final + note.
## 4. Lots B/F
Backend : **B1** domaine (variante+provider_key, VO HttpChatConfig, champ chat_http, port ToolInvoker/ToolSpec/ToolInvocationError ; tests sérialisation zéro-régression, round-trip, invariants ; zéro I/O). **B2** adapter infra openai_compat (reqwest/rustls, parse_* purs, mapping ReplyEvent+erreurs, résolution clé env ; tests fixtures OpenAI/Ollama, erreurs via wiremock, pas de fuite payload). **B3** boucle outils bornée + persistance/rechargement transcript run-dir (reprise) + dégradation tools non supporté (tests ToolInvoker fake, reprise=rechargement, plafond). **B4** routing (bras match) + composition root (with_tool_invoker, impl ToolInvoker→OrchestratorService) + profil de référence "Ollama / OpenAI-compatible local model" éditable (tests factory route, supports true, probe indispo→Start).
Frontend : **F1** types TS HttpChatConfig + champ chatHttp? + adapter "openAiCompatible". **F2** wizard/settings : sélectionnable comme Claude/Codex, form endpoint/model/apiKeyEnv/timeouts, validation miroir backend (first-run/profile.ts isValidEnvVar, URL, non-vide), apiKeyEnv = nom de var jamais clé. **F3** cellule = chemin chat structuré existant (dérivé de is_selectable, "gratuit" si DTO respecté) + erreur "endpoint indisponible" propre sans bloquer UI (tests RTL gateways mock).
Frontière B↔F : **DTO AgentProfile** (déjà le pont IPC) porte structuredAdapter:"openAiCompatible" + chatHttp{endpoint,model,apiKeyEnv?,requestTimeoutMs?,connectTimeoutMs?,maxToolIterations?}. Sélectionnabilité + rendu chat dérivés du DTO existant, pas de nouveau canal ni commande Tauri au cœur (le seed du profil de référence peut passer par le CRUD profils existant — à confirmer B4/F3).
## 5. Vigilance régression / invariants
1. Zéro régression sérialisation (skip_serializing_if=Option::is_none ; round-trip Claude/Codex bit-identique — BLOQUANT). 2. Isolation code : nouvel adapter = fichier neuf ; INTERDIT de toucher claude.rs/codex.rs/process.rs ; seul diff existant = 1 bras match factory.rs + champs optionnels domaine. 3. Contrat flux : exactement un Final terminal ; Heartbeat/ToolActivity/TextDelta/RateLimited non terminaux (conformance.rs doit couvrir le nouvel adapter). 4. Frontière domaine : reqwest UNIQUEMENT infra ; aucun type HTTP en domaine/application ; ToolInvoker port pur. 5. Nouvelle dépendance AppImage : reqwest rustls-tls (pas OpenSSL, spike §13-2). 6. Non-mélange ids : conversation_id()=None ; transcript clé-agent ; distinct de LS6. 7. Sandbox : pas de process spawné ⇒ SandboxEnforcer OS = no-op pour le tour (pas une régression) mais les outils via ToolInvoker gardent le gating orchestrateur.
## 6. Décisions produit remontées à Main (NON tranchées par Architect)
1. Surface d'outils v1 : parité totale immédiate vs sous-ensemble curaté (fiabilité tool-calling des modèles locaux). Reco : parité + garde-fou max_tool_iterations.
2. Streaming SSE dès v1 (reco, parité observabilité live) vs non-streaming (repli si endpoint ne stream pas).
3. Nom canonique variante : `OpenAiCompatible` (reco, décrit le protocole ; provider_key figé) vs `LocalChat` (formulation ticket).

View File

@ -0,0 +1,29 @@
---
name: ticket16-menus-floating-delivered-devfrontend
description: memory note ticket16-menus-floating-delivered-devfrontend
metadata:
type: project
---
# Lot A / #16 — MenuBar + FloatingWindow livré (DevFrontend)
Branche `feature/ui-menus-floating` (base develop 0218e32), non commité, avant merge Git. `vitest` **vert** : 54 fichiers / 527 tests. `tsc --noEmit` exit 0.
## UX pass validée utilisateur (barre unique + sélecteur projet) — APPLIQUÉE
- **Barre unique** : plus de MenuBar séparée dans App.tsx. Une seule MenuBar dans `ProjectsView` regroupant `[ Projet: <actif> ▾ ] File ▾ View ▾ Settings ▾`.
- **Sélecteur de projet permanent** : premier menu de la barre, label = projet actif, items = tous les projets connus (`vm.projects`) → `selectProject(id)` (active l'onglet si ouvert, sinon `openProject`). Accessible même sous projet actif, sans ouvrir File→Projects….
- **Settings → AI Profiles** déplacé dans la barre unique : toggle `showSettings` LOCAL à ProjectsView ; le main area swap vers `<ProfilesSettings/>` pendant que la barre reste visible (permet de refermer). App.tsx ne gère plus showSettings ni ProfilesSettings (juste firstRun ? wizard : ProjectsView).
## Fichiers (état final)
- **Créés (lot A initial)** : `shared/ui/FloatingWindow.tsx`, `shared/ui/MenuBar.tsx`, `shared/ui/FloatingWindow.test.tsx`.
- **Modifiés** : `shared/index.ts`, `shared/ui/zIndex.ts` (réutilisé), `app/App.tsx` (barre + showSettings + ProfilesSettings retirés), `features/projects/ProjectsView.tsx` (barre unique : projectMenu+File+View+Settings, showSettings local, ProfilesSettings dans main), `features/projects/projects.test.tsx` (+2 tests : sélecteur projet, toggle AI Profiles), `features/projects/ProjectsView.ls7.test.tsx`.
## Invariants conservés
- FloatingWindow (focus-trap+Échap+backdrop, z=floatingWindow 50), MenuBar dropdowns z=menuDropdown 40, toasts z=toast 70, zIndex canonique réutilisé.
- TOUS les panneaux atteignables via **View** (context, work, tickets, agents, templates, skills, perms, memory, git) + File→Projects…. Critère d'acceptation respecté.
- Non touché : `shouldShowWritePortalVeil`, exclusion write-portal↔F3, overlays in-cell, TicketPicker (#18).
## Notes
- ProfilesSettings rendu DANS le main de ProjectsView (barre + onglets projet restent visibles au-dessus) au lieu du swap plein-écran App précédent — meilleure continuité, barre unique toujours accessible.
- Pas de run live Tauri (couverture RTL mock-driven). DoD vitest satisfaite.
QA/utilisateur revalident avant merge Git.

View File

@ -0,0 +1,9 @@
---
name: ticket17-focus-trap-fix-merged-develop
description: Correctif de la perte de focus dans l'édition de ticket (#17) livré sur develop via feature branch dédiée.
metadata:
type: reference
---
Bug #17 : saisie une lettre à la fois dans le formulaire d'édition de ticket, cause = focus-trap `FloatingWindow` avec `useEffect` dépendant de `[onClose]` (closure recréée à chaque render). Fix DevFrontend : `onCloseRef` + effet focus-trap mount-only (`[]`).
Topologie Git : branche `feature/ticket17-focus-trap-fix` depuis `develop`, commit `4cccd40` (2 fichiers frontend seulement), merge `--no-ff``develop` = `2f8467e`, branche supprimée. Vert : tsc clean, 531/531 + test de régression. Voir [[ticket16-menus-floating-delivered-devfrontend]], [[ui-rework-sprint-scoping-contracts]].

View File

@ -0,0 +1,28 @@
---
name: ticket18-picker-delivered-devfrontend
description: memory note ticket18-picker-delivered-devfrontend
metadata:
type: project
---
# Lot B / #18 — TicketPicker livré (DevFrontend)
Branche `feature/ticket-picker-popup` (base develop d97abb1). `vitest` **vert** : 53 fichiers / 515 tests, dont 8 nouveaux (`TicketPicker.test.tsx`) + 29 `tickets.test.tsx` inchangés (valide la non-régression du listing après extraction). `tsc --noEmit` clean.
## Fichiers
- **Créés** : `shared/ui/zIndex.ts`, `features/tickets/TicketFacetsBar.tsx`, `features/tickets/useTicketSearch.ts`, `features/tickets/TicketPicker.tsx`, `features/tickets/TicketPicker.test.tsx`.
- **Modifiés** : `shared/index.ts` (export zIndex), `features/tickets/index.ts` (barrel), `features/tickets/TicketsPanel.tsx` (consomme `TicketFacetsBar`).
## Écarts de contrat à trancher (signalés à Main)
1. **Emplacement TicketPicker** : placé sous `features/tickets/`, PAS `shared/ui`, car il dépend du domaine ticket + gateways DI (shared/ui = design system pur). Conforme à tous les autres overlays (SprintManager, MemoryEditor…). Seul `zIndex.ts` (niveau design-system) est dans shared/ui.
2. **`TicketPickerResult.id`/`number`** : `ticket_list`/`TicketSummary` ne portent NI `id` NI `number`. Résolus par un `ticket_read` unique **à la sélection** (`useTicketSearch.resolve`). `number` serait dérivable du ref mais `id` exige la lecture. → contrat `{ref,id,number,title,status,priority}` respecté sans nouveau endpoint. Écart backend candidat : ajouter id/number au summary (dette basse).
3. **`zIndex.ts` provisoire** : lot A (#16) introduit le canonique en parallèle. Valeurs alignées sur l'échelle figée (menuDropdown=40, floatingWindow=50, floatingWindowNested=60, toast=70). **À réconcilier au merge** (garder un seul module). Picker monté à `floatingWindowNested=60`.
## Notes d'impl
- G3-bis respecté : `cursor` traité en token opaque (jamais construit/incrémenté ; relayé verbatim via `loadMore`).
- `excludeRefs` filtré client-side après réception de page.
- `useTicketSearch` léger (pas de groupement sprint, pas de souscription events, debounce 200ms) — ne réutilise pas `useTickets`.
- `TicketFacetsBar` = recherche + facettes statut/priorité (labels aria préservés : `search tickets`, `filter status …`, `filter priority …`, `clear filters`). Le select assignee reste dans `TicketsPanel` (hors périmètre picker).
- Focus-trap inline (Tab cycling + Escape + restore focus) car aucun util partagé n'existe ; G5 respecté (montage niveau chrome par le consommateur #17/#19).
- `selectionMode:"multi"` câblé de façon extensible (footer réservé) mais non actif en V1.
QA repasse derrière. Consommateurs #17 (link) et #19 (sprint) importent `TicketPicker` depuis `@/features/tickets`.

View File

@ -0,0 +1,11 @@
---
name: ticket25-assistant-sandbox-eacces-rootcause
description: Root cause et contrat de fix du EACCES (os error 13) au spawn de la CLI d'un assistant de ticket — le preset SandboxPlan::project_read_only handle la classe exec Landlock.
metadata:
type: reference
---
**Symptôme** : ouvrir un assistant de ticket (Codex ou Claude), premier message ⇒ `process error: agent session start failed: <cli>: Permission non accordée (os error 13)`. Indépendant de la CLI.
**Root cause** : `OpenTicketAssistant::execute` (`application/src/ticket_assistant.rs:97`) fabrique un plan bespoke via `SandboxPlan::project_read_only` (`domain/src/sandbox.rs:153`) = grant **RO** sur `project_root` + RW sur le run_dir, posture `Deny`. Dans l'adapter Landlock (`infrastructure/src/sandbox/landlock.rs:70`), un grant RO *handle* la classe read, et `AccessFs::from_read(V1)` **inclut `AccessFs::Execute`**. Le binaire CLI vit hors du project root ⇒ `execve` refusé ⇒ EACCES au `spawn()` sandboxé (`process.rs:248`). Le chemin workspace n'a pas le bug car son plan vient de `compile_sandbox_plan` sous posture `Allow`/write-only ⇒ l'adapter no-op ou ne handle que write, read/exec restent ouverts. Piège déjà documenté dans l'adapter (« enrichment is the launch-path's concern LP4-2 »).
**Fix (backend-pur, zéro port/DTO)** : remplacer le preset par un **write-fence** — ne jamais handle read/exec (pas de grant RO), handle write seul, RW-grant = run_dir + state/home CLI + temp, posture Deny ⇒ écriture projet refusée, binaire/libs/home lisibles-exécutables. Fallback sûr : passer `None` à `factory.start` pour la surface ticket (garde policy MCP + projection advisory). Garde-fous fonctionnels réels = policy MCP `idea_ticket_read/update*` + LP3, pas l'OS-sandbox. Lié à [[ui-rework-sprint-scoping-contracts]].

View File

@ -0,0 +1,17 @@
---
name: ticket28-f1-frontend-delivered
description: Lot F1 du ticket #28 livré sur feature/ticket28-firstrun-detect-hang — invariant busy/detectProfiles, flag detecting, timeout UI, test de non-régression.
metadata:
type: reference
---
Lot **F1** (frontend) du bug #28 implémenté sur `feature/ticket28-firstrun-detect-hang` (non committé, Git s'en charge).
**Invariant posé** : dans `frontend/src/features/first-run/useFirstRun.ts`, le retour de `busy` à `false` ne dépend d'aucun appel à `detectProfiles`. `busy` n'entoure plus que `firstRunState()` (reload) et `configureProfiles()` (finish). Toute future régression qui remet un `await profile.detectProfiles(...)` sur un chemin gouvernant `busy` re-gèle le wizard.
**Mécanisme** : `runDetection(candidates, { preselect, reportError })` factorise auto-détection et bouton manuel. Le flag `detecting` est relâché par un `setTimeout(DETECT_TIMEOUT_MS = 3_000)`, **jamais** par la promesse — car le `finally` d'un `await` sur une promesse jamais settled (panic Rust ⇒ pas de réponse IPC) n'est jamais atteint. Un compteur `detectRun` invalide les rounds périmés (re-clic, unmount).
**Piège UI à retenir** : `<Button loading>` implique `disabled` dans le design system (`shared/ui/Button.tsx:46`). Donc `detecting` ne peut pas être surfacé via `loading` sur les boutons Save/Detect sans réintroduire le grisage — il est rendu comme un `role="status"` « Detecting… » séparé.
**Point de vérité QA** : `FirstRunWizard.test.tsx`, describe « detection that never answers (ticket #28) », gateway `HangingDetectGateway` dont `detectProfiles` renvoie `new Promise(() => {})`. Vérifié à teeth : en réintroduisant `setBusy(true); await runDetection(...)` dans `reload`, 2 des 3 tests échouent avec exactement le symptôme du ticket.
Vert : `npx vitest run` 59 fichiers / 569 tests, `npx tsc --noEmit` exit 0. Pas de lint dans ce projet (aucun eslint config ni script). F1 débloque l'UI mais **B1 (DevBackend, `AgentRuntime::detect` sync→async) reste requis** pour que la détection remonte réellement un résultat.

View File

@ -0,0 +1,19 @@
---
name: ticket28-firstrun-detect-hang-scoping
description: Cadrage hexagonal du bug bloquant first-run (#28) — panic block_on imbriqué dans detect + découplage busy/détection, contrats et point de vérité QA.
metadata:
type: reference
---
Bug high #28 (build 0.3.0 / develop 3c6cd04) : au first-run, « Save and continue » ET « Detect installed CLIs » restent grisés → wizard impossible à terminer.
**Cause racine unique (bloquante) — CONFIRMÉE source :** `AgentRuntime::detect` est resté `fn` synchrone (`domain/src/ports.rs:754`, impl `infrastructure/src/runtime/mod.rs:182`) alors que le trait est déjà `#[async_trait]`. L'impl bridge le spawner async via `futures_block_on`/`block_on` (mod.rs:228-237), appelé DANS la commande async Tauri `detect_profiles``DetectProfiles::execute` (async) → `detect()` sync sur la même task → tokio panic « Cannot start a runtime from within a runtime ». Un panic dans une commande async Tauri = **aucune réponse IPC** → promesse `invoke` jamais résolue/rejetée → le try/catch interne (`useFirstRun.ts:79-90`, n'attrape que les rejets) ne l'attrape pas → `finally` jamais atteint → `busy` figé true → boutons `disabled: vm.busy` grisés à vie. Se déclenche au 1er profil sondé, toute machine. Déclencheur nouveau de #14 : auto-détection à l'ouverture dans `reload`.
**Cause secondaire (NON bloquante) :** profil `ollama-openai-compatible` (command `openai-compatible`, `detect:None`) → fallback `openai-compatible --version` (binaire absent) → `available:false` erroné (spawn échoue vite, pas de hang). Endpoint HTTP sondé comme un CLI = catégorie-fausse. base_url dispo via `HttpChatConfig` (http://localhost:11434/v1). Précédent réutilisable : `infrastructure/src/store/embedder.rs:401 detect_ollama` (reqwest, timeout 800ms, feature vector-http).
**Fix figé — Backend (DevBackend) :** B1 promouvoir `detect` en `async fn` (supprimer futures_block_on entièrement), maj `DetectProfiles::execute` (.await) + commande. B2 `tokio::time::timeout` borné par probe. B3 brancher endpoint-kind (chat_http / StructuredAdapter::OpenAiCompatible) → probe HTTP best-effort borné de base_url (jamais d'erreur dure) ; CLI-kind = `detection_spec` INCHANGÉ (zéro-régression Claude/Codex). Aucun nouveau port/adapter.
**Fix figé — Frontend (DevFrontend) :** F1 découpler `busy` de la détection : `busy` n'entoure que `firstRunState()`, relâché dès rendu des lignes. Auto-détection = étape détachée non bloquante suivie par un flag distinct `detecting` (ne grise pas Save), avec timeout UI (~3s). Invariant : retour de busy à false ne dépend JAMAIS de detectProfiles.
**Contrats :** port `AgentRuntime::detect` sync→async (validé Architecture). DTO detect_profiles INCHANGÉ (available:bool). Catalogue ollama : detect reste None, détection décidée par nature endpoint.
**Point de vérité QA :** Backend = test `#[tokio::test]` multi-thread sur DetectProfiles::execute (échoue au panic sur code actuel, passe après B1) + endpoint sans spawn CLI + CLI Claude/Codex inchangé. Frontend = RTL avec mock detectProfiles jamais résolu → Save+Detect actifs dès firstRunState. Live = first-run machine sans Claude/Codex, Ollama coché, boutons actifs, Save termine (rebuild AppImage obligatoire). F1 seul débloque déjà ; B1 supprime la cause racine ; les deux requis pour vert.

View File

@ -0,0 +1,18 @@
---
name: ticket4-announcements-frontend-f1f2f3
description: Topologie du frontend des annonces inter-agent (store borné, preview demandeur filtré requester, overlay cible piloté par le busy/idle par agent) — réintégré sur base develop.
metadata:
type: project
---
Branche `feature/agent-live-announcements` (base develop `ebd992e`, non commité). Frontend réintégré depuis `backup/announcements-work-20260704`. Feature `frontend/src/features/announcements/` :
- `announcementsStore.ts` : index pur `byTarget[target][ticketId]->Announcement[]`, `appendAnnouncement` borné (cap 50), `purgeAllForTarget` (au busy:false), sélecteurs target/requester. Éphémère, jamais persisté. (`purgeTurn` du backup RETIRÉ — plus de signal final sur develop.)
- `AnnouncementsProvider.tsx` (monté dans App.tsx) : 1 abonnement `system.onDomainEvent`. Deux signaux : CONTENU = `agentAnnouncement`→append ; CYCLE DE VIE = map `busy` par agent, seule autorité de montage/retrait, alimentée par `agentBusyChanged{agentId,busy}` + hydratée du read-model `ProjectWorkState.agents[].busy` via `useHydrateAgentBusy(projectId,agentId)` (seed « si absent » → event live gagne). `busy:false`→purgeAllForTarget. Hooks `useTargetAnnouncements` (active=busy[target]), `useRequesterAnnouncements`, `useHydrateAgentBusy`. Hors provider = store inerte.
- F3 `TargetAnnouncementsOverlay(projectId,agentId)` monté dans **`LayoutGrid` LeafView** (PAS ConversationCell — absent sur develop ; la cellule agent est le PTY `TerminalView`). Overlay `pointer-events-none`, z-20, au-dessus du write-portal overlay existant. F2 `AnnouncementsPreview` par ligne d'`AgentsPanel` (requester==a.id).
ADAPTATION develop appliquée : `agentTurnEvent{final}` N'EXISTE PAS sur develop → branche de purge retirée du provider, type NON réintroduit dans domain, 2 tests agentTurnEvent supprimés. Autorité unique F3 = busy. Confirmé présents sur develop : `agentBusyChanged{agentId,busy}` (domain/index.ts) et `AgentWorkState.busy: {state:"idle"}|{state:"busy";ticket;sinceMs}`. DTO annonce backend confirmé (events.rs, test JSON à events.rs:927) : `{type:"agentAnnouncement",projectId,requester("user"|agentId),target,ticketId,text,atMs}` — mirroir ajouté dans domain/index.ts.
Exclusion des voiles (arbitrage [[ticket4-overlay-composition-leafview]]) : le voile write-portal (injection PTY) et l'overlay F3 partagent le conteneur relatif du LeafView. Prédicat pur exporté `shouldShowWritePortalVeil(agentPinned, writePortalOverlay, busyActive) = agentPinned && overlay && !busyActive` ; F3 se gate sur le même `busyActive` (`useTargetAnnouncements(agentId).active`, remonté au LeafView). Résultat : exactement UN voile, F3 prioritaire. Purement visuel — `portal.isSuspended()`/l'injection PTY (TerminalView) INCHANGÉS. Testé exhaustivement (table de vérité) dans `overlayExclusion.test.ts`.
Écarts tranchés à signaler : (1) F3 hébergé dans la cellule PTY (LeafView) faute de ConversationCell ; (2) busy est l'autorité brute : sur develop busy vient du mediator (délégations/inter-agent), pas du typing PTF humain, donc proxy correct, mais surveiller un éventuel over-trigger ; (3) F2 filtre requester seul (pas de ticketId par ligne) = garde anti-fuite.
Build vert, 51 fichiers / 481 tests vitest verts.

View File

@ -0,0 +1,25 @@
---
name: ticket4-overlay-composition-leafview
description: Règle de composition des deux voiles plein-cellule de LeafView (write-portal injection PTY vs F3 annonces busy) — exclusion mutuelle avec priorité F3, arbitrée pour éviter le double voile.
metadata:
type: reference
---
Arbitrage layout Architect 2026-07-04 (branche feature/agent-live-announcements, base develop ebd992e, 481 tests verts).
**Problème vérifié :** LeafView héberge DEUX voiles plein-cellule inset:0 dans le même conteneur relatif :
- write-portal (LayoutGrid.tsx:841, zIndex:4, rgba(0,0,0,0.45), « Un agent est en train de parler… ») = délégation injectée dans le PTY de l'agent (§20.3, flag `overlay` de useWritePortal).
- F3 TargetAnnouncementsOverlay (z-20, bg-canvas/85, « …en train de LUI parler… » + réflexions) = agent busy (agentBusyChanged mediator).
Ils rendent le MÊME événement depuis deux signaux et montent ENSEMBLE sur le chemin d'injection inter-agent (overlay+busy) → double voile empilé + bannières dupliquées = rendu cassé, chemin nominal.
**Décision (DevFrontend, layout, zéro backend) :** exclusion mutuelle, un seul voile à la fois, priorité F3 :
- busy(agent) → F3 seul ; voile write-portal gated `agentId!=null && overlay && !busyActive`.
- sinon overlay → write-portal seul (repli fenêtre sans busy).
- jamais les deux.
Sécurité : on ne touche que le visuel ; la suspension des frappes (portal.isSuspended(), TerminalView.tsx:182) reste ; F3 est pointer-events-none.
**Couplage point 3 :** F3 DOIT rendre la bannière sur busy même à 0 annonce (déjà: if(!active) return null, TargetAnnouncementsOverlay.tsx:56), car F3 absorbe le voile write-portal — une garde « ≥1 annonce » rouvrirait le trou d'indication. Pas de garde.
**F2 (point 4) :** filtre requester==id de ligne suffit en V1 (isolation fil partagé) ; ticketId optionnel dormant.
**F3 hébergement (point 1) :** conteneur LeafView validé (pas de ConversationCell sur develop).
Liens : [[ticket4-restart-on-develop-ebd992e]], [[ticket4-f3-overlay-driven-by-agentbusychanged]], [[inter-agent-live-context-shared-per-agent]].

View File

@ -0,0 +1,20 @@
---
name: ticket4-restart-on-develop-ebd992e
description: Cadrage du redémarrage des annonces inter-agent sur la base pré-canonique develop ebd992e — ce qui existe, l'écart live, la réutilisabilité du backup et le découpage B/F.
metadata:
type: reference
---
Redémarrage #4 sur base réalignée main=develop=feature/background=feature/agent-live-announcements=ebd992e (pré-canonique). Vérifié sur le dépôt le 2026-07-04.
**Signaux sur develop :**
- Émission intermédiaire : AUCUNE. codex.rs draine via run_turn BATCH (process.rs:268/321 lit ligne-à-ligne mais collecte en Vec, renvoie à EOF ; codex.rs:260 Box::new(events.into_iter())). structured.rs:266-271 jette TextDelta/ToolActivity/Heartbeat, ne renvoie que Final.
- Busy PAR AGENT : PRÉSENT ET COMPLET. DomainEvent::AgentBusyChanged{agent_id,busy} (domain/events.rs:171), autorité mediator (infrastructure/input/mod.rs:433), relay (app-tauri/events.rs:484), front event domain/index.ts:22 + read-model agents[].busy {state,ticket,sinceMs} (:220,304) réconcilié reboot. ⇒ autorité overlay F3 dispo telle quelle.
- Fix Final : PRÉSENT (62915ee) mais dégrade les agent_message intermédiaires en ToolActivity{label} en JETANT leur texte, et reste batch → inexploitable pour annonces.
**Écart live (faisable SANS moteur canonique) :** run_turn lit déjà incrémentalement ; le batch n'est qu'un artefact de sa signature ->Vec. Ajouter un tap Option<mpsc::Sender<String>> dans run_turn/drain/drain_sandboxed (boucle existante) ; l'orchestrateur (qui détient requester/target/ticket) publie DomainEvent::AgentAnnouncement au fil de l'eau. Thread Landlock n'envoie que des lignes brutes ; publication bus hors thread. Pas de spawn_turn/AgentTurnEvent/ConversationId.
**Réutilisabilité backup (backup/announcements-work-20260704, 691b2ce) :** frontend features/announcements/ (store borné, provider, AnnouncementsPreview=F2, TargetAnnouncementsOverlay=F3) réutilisable ; F3 sur busy = intact sur develop. Dépend d'agentAnnouncement{requester,target,ticketId,text,atMs} (absent→B) et agentTurnEvent{final} (absent, secondaire → RETIRER du store). Backend backup non réutilisable (canonique) ; contrat/attribution repris.
**Découpage :** B1 domaine/appli (ReplyEvent::Announcement + DomainEvent::AgentAnnouncement + publish au drain) ; B2 infra = CŒUR (tap mpsc process.rs + codex émet Announcement{text} live) ; B3 relay/DTO ; F1 store (hook final) ; F2 preview (filtre ticketId+requester==self) ; F3 overlay (réutilisé, busy seul autorité).
Liens : [[ticket4-f3-overlay-driven-by-agentbusychanged]], [[ticket4-announcements-reintegration-topology]], [[inter-agent-announcements-feature-and-codex-final-bug]], [[inter-agent-live-context-shared-per-agent]].

View File

@ -0,0 +1,15 @@
---
name: ticket54-f1-model-download-overlay-frontend
description: Overlay plein-cellule de préparation du serveur modèle local + progression (barre/%/bytes/source), corrélation factorisée, priorité de voile.
metadata:
type: reference
---
Ticket #54 livré et vert côté frontend (F1 = overlay, F2 = progression).
**Contrat** : `ModelServerStatus.downloading { downloadedBytes|totalBytes|percent|source: number|string|null }` (`frontend/src/domain/index.ts`), miroir exact de `ModelServerStatusDto` (`crates/app-tauri/src/events.rs`). Event `modelServerStatusChanged` passé tel quel — pas de mapping de désérialisation.
**Factorisation** dans `frontend/src/features/agents/modelServerLaunch.ts` : `correlateModelServerStatus` (chaîne `agentId→profileId→opencode.localModelServerId→serverId`), `modelServerOverlayText` (titre+gating de voile), `useModelServerLaunchState(projectId)` (source autonome pour LeafView), et F2 : `formatBytes` (SI base 1000 : o/Ko/Mo/Go) + `describeModelServerDownload``{percent(clamp 0..100 ou null=indéterminé), bytesLabel, source}` (null hors `downloading`). AgentsPanel réutilise le helper.
**Overlay** dans `LeafView`/`LayoutGrid.tsx` (`data-testid=model-server-overlay`, `zIndex CELL_Z.veil`) : titre + barre `role=progressbar` (aria-valuenow seulement en déterminé, sinon barre indéterminée via keyframe `model-server-indeterminate` dans `theme.css`) + label `"X % · A Mo / B Mo"` + ligne source HF. `starting`/`probing` = titre seul, pas de barre. **Priorité : voile modèle au-dessus** des voiles write-portal + F3 (les deux gatés sur `!modelServerOverlay`). Voir [[ticket4-overlay-composition-leafview]].
Tests : `LayoutGrid.modelServerOverlay.test.tsx` (F1+F2, dont multi-cellules & indéterminé) + unitaire pur `modelServerLaunch.test.ts`. Non-régression `src/features/agents src/features/layout` = 202 verts ; `npx tsc --noEmit` clean.

View File

@ -0,0 +1,19 @@
---
name: ticket56-visibleelsewhere-derives-from-layout
description: Fix frontend du sélecteur d'agent : la désactivation « visible ailleurs » doit venir du layout courant, pas du dernier nodeId du live registry.
metadata:
type: reference
---
Bug #56 : agent X affiché en cellule A → A bascule sur Y → X désactivé (« visible ailleurs ») en cellule B alors que X n'est plus affiché nulle part.
Cause : le live registry garde X→nodeId A même après le swap ; `LayoutGrid.visibleElsewhere` lisait `live.nodeId` comme « affiché ici ».
Fix (frontend pur, ne tue pas la session) dans `frontend/src/features/layout/LayoutGrid.tsx` :
- `visibleElsewhere(candidate)` dérive du **layout courant** : truthy uniquement s'il existe une feuille visible ≠ cellule courante avec `leaf.agent === candidate`. Retourne `{ nodeId }`.
- `backgroundLive` utilise `!visibleElsewhere(candidate)` au lieu de `!visibleNodeIds.has(live.nodeId)` ; garde `live.nodeId === id` (anti-boucle self-launch).
- onChange du select : même dérivation ; sélection inchangée (attachLiveAgent si sessionId sinon setCellAgent).
- Prop `visibleNodeIds` supprimé (devenu mort) de tout le threading LayoutGrid→NodeView→Split/Grid/Leaf.
Invariant préservé : agent réellement épinglé dans une cellule visible reste NON sélectionnable ailleurs.
Piège de test : un test qui mocke `listLiveAgents` avec un nodeId visible mais **sans** épingler l'agent dans ce leaf ne désactive plus rien — il faut `setCellAgent` réel. Tests dans `singletonAgent.test.tsx` (describe « ticket #56 » + ajustement R0d). Suite frontend : 706 verts.

View File

@ -0,0 +1,16 @@
---
name: ticket7-f2-delegated-limit-no-agentratelimited
description: Écart backend ticket #7/F2 — le rendez-vous délégué n'émet pas AgentRateLimited ni n'arme la reprise pour la cible limitée.
metadata:
type: project
---
Ticket #7, volet inter-agent. Constat F2 (frontend DevFrontend) sur `feature/session-limit-interagent`.
Le chemin délégué (rendez-vous headless) **n'appelle jamais** `session_limit_service.on_rate_limited(target, …)`. Dans `crates/application/src/orchestrator/service.rs:1876-1915`, la branche `TargetRateLimited` publie **uniquement** `DomainEvent::DelegationRateLimited{requester, target, resets_at_ms}` puis retourne `AppError::TargetRateLimited`. Les deux seuls sites de `on_rate_limited` sont les chemins humains PTY/chat (`crates/app-tauri/src/commands.rs:1409` et `:1535`).
**Conséquence** : quand la cible B est limitée **via une délégation**, aucun `AgentRateLimited`/`AgentResumeScheduled` n'est émis pour B ⇒ le badge mono-agent de B (piloté par `limitByAgent` dans `useAgents`) ne s'affiche pas et aucune reprise auto n'est armée pour B. Contredit le commentaire de `crates/app-tauri/src/events.rs:370-372` (« the target resume flow is surfaced by the existing agent limit events »).
**Why:** F2 supposait « même flux AgentRateLimited ». Le rendu frontend du badge est sain (re-render + bouton Annuler OK) ; l'écart est backend.
**How to apply:** correction = backend (appeler `on_rate_limited` pour la cible dans la branche rendez-vous, avec node/conversation adéquats). Ne PAS dériver le badge de B depuis `delegationRateLimited` côté front : cet event ne porte ni sémantique de reprise armée ni node_id/conversation_id pour armer le réveil → inventerait un contrat. Le côté DEMANDEUR (A) est, lui, bien couvert par F1 (`suspendedDelegationsByRequester`). Voir [[session-limit-handling-design]].

View File

@ -0,0 +1,18 @@
---
name: ticket74-f1-web-bundle-transport-seam
description: Deux artefacts Vite (dist/dist-web), mode `web` portable Windows, et le câblage anti-divergence de la constante de transport.
metadata:
type: reference
---
Le frontend produit **deux** artefacts Vite depuis les mêmes sources :
- `frontend/dist` → transport `tauri` (desktop, `build.frontendDist`) — `npm run build`.
- `frontend/dist-web` → transport `http` (packagé en ressource Tauri `web/`) — `npm run build:web`.
- `npm run build:bundle` produit les deux : c'est le point d'entrée du packaging Tauri.
**Le mode web passe par `--mode web` + `frontend/.env.web` (`VITE_TRANSPORT=http`), jamais par un préfixe inline `VITE_TRANSPORT=http vite build`** : non portable Windows/NSIS, cible active du bundle. `.env.web` est versionné (non gitignoré) ; `dist-web/` est ignoré.
**Anti-divergence de `__IDEA_TRANSPORT__`** : `frontend/src/app/transport.ts` expose `transportFromEnv(value)`, importé à la fois par `di.tsx` (`resolveTransport`/`shouldUseHttp`) et par `vite.config.ts`, qui lui passe `VITE_TRANSPORT` via `loadEnv(mode)`. Même variable + même prédicat = une seule source. Modifier le prédicat déplace constante et runtime ensemble. `vitest.config.ts` réplique le `define` via le même helper.
La constante est **publiée sur `window` dans `main.tsx`** : `define` ne remplace que les occurrences existantes et une constante non référencée serait tree-shakée ; l'affectation est un effet de bord qui survit à la minification. Elle reporte le transport hors mock (`VITE_USE_MOCK` reste un switch distinct).
Pour identifier le transport d'un bundle bâti : `grep -o '__IDEA_TRANSPORT__="[a-z]*"' dist*/assets/*.js`. **Ne pas se fier à la présence de `__TAURI_INTERNALS__` ni à un nom de symbole minifié** : les deux jeux d'adapters sont présents dans les deux bundles, un grep ne les discrimine pas.

View File

@ -0,0 +1,15 @@
---
name: tickets-frontend-v1-done
description: État du frontend du système de tickets — livré, vert, décisions de contrat clés.
metadata:
type: project
---
Frontend V1 du système de tickets livré sur `feature/issue-ticket-system` (non commité, attend QA/Git). Build + 459 tests verts.
Décisions figées :
- Port nommé `TicketGateway` (pas IssueGateway) — aligné sur le wire `ticket_*`/`TicketDto` et le terme visible « ticket ».
- Statut/priorité côté UI passent par `ticket_update` : `ticket_update_status`/`ticket_update_priority` ne sont PAS des commands Tauri (MCP-only, non enregistrées dans `lib.rs`).
- Conflit de version = `code:"INVALID"` + message contenant `"issue version conflict"` (détection par message, pas de code dédié). Voir `isTicketVersionConflict`.
- F7 = `#ref` cliquables + linkification desc + bouton clipboard « Travaille sur le ticket #N : <titre> » (pas d'écriture terminal directe en V1).
Surface : `features/tickets/` (panel liste + overlay détail/carnet/liens/assign), onglet sidebar « Tickets » dans ProjectsView entre Work et Agents, `MockTicketGateway` émet les events `Issue*`. Voir [[checkpoint-issue-ticket-backend-v1-done]] et [[issue-ticket-system-design]].

View File

@ -0,0 +1,15 @@
---
name: tickets-t3-frontend-validation-verdict
description: Verdict de validation frontend du fix T3 (projection backgroundTasks par agent) sur feature/background-tasks-first-class — vert, avec 3 désalignements de contrat mineurs.
metadata:
type: reference
---
Validation frontend du fix T3 (ticket #1), branche `feature/background-tasks-first-class`, 2026-07-03.
**Verdict : vert.** `npx vitest run` = 460/460, `npm run build` (tsc --noEmit + vite) OK. Le front était déjà câblé pour Cancel/Retry ; contre le vrai payload backend le mapping d'états et l'activation des boutons fonctionnent.
**Contrat backend réel** (`crates/app-tauri/src/dto.rs::AgentBackgroundTaskStateDto`, camelCase) : `taskId, kind, state, exitCode?, summary?, stdoutTail?, stderrTail?, createdAtMs, updatedAtMs`. `state` ∈ queued|running|waiting|completed|failed|cancelled|expired ; `kind` ∈ command|headlessRendezvous|sessionResume|maintenance. **Pas** de `status`, `ownerAgentId`, `projectId`, ni `finishedAtMs`.
**Fait :** FE-1 rendu explicite `queued`/`waiting``pending` dans `normalizeBackgroundStatus`. FE-2 ajout d'un test sur le vrai shape dans `src/features/workstate/workstate.test.tsx` (le mock passe par le vrai `normalizeProjectWorkState`, donc pas de faux-vert possible).
**Écarts de contrat à arbitrer (dette, panneau non modifié) :** (1) `ProjectWorkStatePanel.tsx` trie sur `finishedAtMs` jamais émis → tri retombe sur taskId, non chronologique ; (2) `summary` backend non porté par `BackgroundCompletion` → ignoré ; (3) `ownerAgentId`/`projectId` absents du payload par-agent (inoffensif : fallback = agentId). Voir [[workstate-background-tasks-projection-fix]] et [[b8-in-app-trigger-run-in-background]].

View File

@ -0,0 +1,15 @@
---
name: tickets-v1-e2e-validated-qa
description: Verdict QA du système de tickets V1 sans attachments sur feature/issue-ticket-system — vert de bout en bout, avec la seule réserve du typage de conflit côté UI.
metadata:
type: reference
---
Validation QA de bout en bout du système de tickets **V1 (sans attachments)** sur `feature/issue-ticket-system` (backend commité `8de7be0`, frontend non commité). **Verdict : OK.**
**Preuves réelles :**
- `cargo build --workspace` OK. Tests verts sur domain/application ; infrastructure et app-tauri verts **sauf la dette pré-existante déclarée** (`orchestrator_watcher::ask_request_surfaces_reply_alongside_detail` + `app-tauri state::mcp_e2e_loopback_tests::*`, tous `Elapsed(())` du rendez-vous, sans rapport avec les tickets). Aucun échec imputable aux tickets.
- Frontend : `npm run build` OK (tsc+vite), `npm test` = **459 passed / 49 files**.
**Invariants prouvés par tests :** `#N` séquentiel jamais réutilisé (`allocator_never_reuses_numbers`) ; create→read round-trip ; carnet remplaçable & scoped `.ideai/tickets/N/carnet.md` ; pas de self-link (`IssueError::SelfLink`, `domain/issue.rs`) ; assignation agent connu seulement (`create_issue_rejects_unknown_assignee`) ; concurrence optimiste (`update_issue_maps_version_conflict`, `IssueStoreError::VersionConflict`) ; énums fermés statut/priorité.
**Réserve non bloquante (dette technique) :** le conflit de version est détecté côté **front** par fragment de message (`message.includes("version conflict")`, `frontend/src/adapters/ticket.ts`, `useTicketDetail.ts`), s'appuyant sur `domain/ports.rs` `#[error("issue version conflict: …")]`. Un code typé `"versionConflict"` existe DÉJÀ mais uniquement sur la surface MCP (`app-tauri/src/tickets.rs:942`) ; le chemin Tauri command renvoie `INVALID`. Recommandation : propager le code typé dans l'enveloppe d'erreur des `ticket_*` commands et brancher l'UI sur `error.code`. Comportement actuel correct et testé, donc non bloquant.

View File

@ -0,0 +1,19 @@
---
name: ui-rework-sprint-delivered-tickets-21-22-23
description: memory note ui-rework-sprint-delivered-tickets-21-22-23
metadata:
type: project
---
Sprint « UI rework » (order 1) bouclé le 2026-07-07. Les 4 premiers tickets (#16/#17/#18/#19) étaient déjà mergés ; les 3 derniers viennent d'être produits et intégrés. Cadrage : [[ui-rework-sprint-tickets-21-22-23-scoping]].
## Livré (tous verts QA par exécution réelle, mergés --no-ff sur develop)
- **#21** tri des tickets (B+F) — merge `37bf7d4`. Backend : `TicketListQuery.sort?:{field:"number"|"priority"|"status"|"title";direction:"asc"|"desc"}`, appliqué avant pagination, tie-break systématique par `number` croissant. Ordres sémantiques : priority low<medium<high<critical ; status open<inProgress<QA<closed ; title casse-insensible. `sort` absent = comportement historique. Frontend : contrôle « Trier par » dans `TicketFacetsBar` (partagé TicketsPanel + TicketPicker), `sort` dans query+queryKey (reset pagination), curseur reste opaque (G3-bis).
- **#22** docking de views (F pur) merge `2323a71`. Nouvelle primitive `shared/ui/DockRegion` + modèle `features/projects/viewPlacement.ts` : `ViewPlacement = "closed"|"floating"|{dock:"left"|"right"}` par PanelId, invariant un-seul-slot. Ne réutilise NI FloatingWindow NI LayoutTree terminaux (monté chrome-level ProjectsView).
- **#23** fenêtres système (B+F) merge `47b1d64` (HEAD develop). Backend app-tauri : commands `open_view_window({panel,projectId})→{label,url,alreadyOpen}` et `close_view_window(...)→{label,closed}`, vraie WebviewWindow chargeant `index.html?panel=&project=`, registre anti-doublon (label `view-{panel}-{projectIdSansTirets}`), event unique `view-window://lifecycle` payload `{kind:"opened"|"focused"|"closed",panel,projectId,label}`, capability étendue à `view-*`. Frontend : port `WindowGateway` (adapter Tauri + mock), entrée panel-only `app/ViewWindow.tsx` routée dans `main.tsx`, `ViewPanelBody` partagé, `ViewPlacement` étendu à `"detached"`, menu Window + bouton ⤢, garde no-direct-invoke.
## Dette basse restée ouverte (hors périmètre, non bloquante)
- Curseur `ticket_list` toujours offset non opaque/stable (cf. [[ui-rework-sprint-scoping-contracts]]) non soldée par #21.
- Persistance restore-au-redémarrage du docking #22 et du placement détaché #23 : follow-up backend différé (V1 = placement éphémère useState).
## État Git
develop @ `47b1d64` ; branches feature/ticket21-22-23 supprimées (mergées). Rien de sortant, `main` intact. Release `develop→main` en attente de validation utilisateur.

View File

@ -0,0 +1,23 @@
---
name: ui-rework-sprint-scoping-contracts
description: Frontières hexagonales et contrats figés du sprint UI rework — les 4 tickets sont frontend-purs, la popup TicketPicker #18, l'échelle z-index et le curseur opaque.
metadata:
type: reference
---
Sprint « UI rework » (order 1), cadré 2026-07-06. Les **4 tickets sont frontend-purs** : aucun nouveau port/read-model/endpoint.
**Fait clé** : filtrage/recherche tickets déjà backend-driven via `TicketGateway.list(projectId, TicketListQuery{statuses[],priorities[],assignedAgentId,text,limit,cursor})` (ports/index.ts:717) → adapter transmet à `ticket_list`. Client ne fait que le groupement par sprint. #18 réutilise ce contrat, pas d'endpoint dédié.
**G4 (contrat ticket_list) levé pour B** après revue DevBackend : statuses/priorities OK (OR intra / AND inter). Deux écarts en DETTE BASSE : (a) `text` ne matche pas `#ref` (tickets.rs:681, issues.rs:322/326/465) ; (b) `cursor` = offset numérique non opaque/non stable inter-pages (tickets.rs:1231). Acceptable pour picker single-select court.
**G3-bis (garde non-breaking)** : TicketPicker/useTicketSearch traitent `cursor` comme token OPAQUE — ne jamais le construire/incrémenter, ne relayer que le cursor de la page précédente. Migration curseur opaque backend = zéro changement frontend.
**Base de code** : pas de routeur ni store global (navigation = useState local App.tsx + ProjectsView.tsx, sidebar inline ProjectsView.tsx:224). Aucune primitive Modal/Portal partagée ; ~7 features hand-roll `fixed inset-0 role=dialog` z-50/z-[60]. Pas de createPortal. #16 introduit `shared/ui/FloatingWindow` (focus-trap, monté niveau chrome) + `shared/ui/zIndex.ts`.
**Contrat #18 figé**`TicketPicker` props `{open, projectId, title?, confirmLabel?, selectionMode?:"single"|"multi" (défaut single), excludeRefs?: TicketRef[] (filtré client-side), initialQuery?: TicketListQuery, onSelect(TicketPickerResult|[]), onClose}`. `TicketPickerResult={ref,id,number,title,status,priority}`. Extraire `TicketFacetsBar` (fieldsets statut/priorité + recherche depuis TicketsPanel.tsx) + hook `useTicketSearch` (ne PAS réutiliser useTickets, trop lourd). #17 et #19 consomment en single.
**Échelle z-index figée (G2)** : in-cell inchangé (chrome 2-5, F3 z-20, write-portal z-4, resume z-5) ; chrome full-window menuDropdown=40, floatingWindow=50, floatingWindowNested=60, toast=70.
**Règle intégration (G5)** : fenêtres flottantes/menus montées niveau chrome (ProjectsView/App), JAMAIS dans LayoutGrid/LeafView. Ne pas toucher shouldShowWritePortalVeil ni exclusion write-portal↔F3 (cf. [[ticket4-overlay-composition-leafview]]). Focus-trap obligatoire (xterm garde le focus clavier).
**Ordre** : A(#16) ∥ B(#18) racines → prioriser B (chemin critique #17+#19) ; D(#19) après B ; C(#17) après A+B. Tout DevFrontend ; DevBackend = ticket dette basse (matching #ref + curseur opaque/stable).

View File

@ -0,0 +1,39 @@
---
name: ui-rework-sprint-tickets-21-22-23-scoping
description: memory note ui-rework-sprint-tickets-21-22-23-scoping
metadata:
type: project
---
Cadrage hexagonal des 3 derniers tickets du sprint « UI rework » (order 1), fait 2026-07-07 par Architect. Les 4 premiers (#16/#17/#18/#19) sont livrés/mergés sur develop — cf. [[ui-rework-sprint-scoping-contracts]]. Ne pas re-cadrer ; produire.
## État de départ vérifié
- Vues (context/work/tickets/agents/templates/skills/permissions/memory/git) = composants React rendus via menu **View**, pilotés par `openPanel: useState<PanelId|null>` (ProjectsView.tsx:127). UNE seule vue à la fois dans un `FloatingWindow` modal centré (ProjectsView.tsx:497).
- `FloatingWindow` = overlay MODAL (fixed inset-0, backdrop, focus-trap, z-50), non déplaçable/non redimensionnable, purement présentationnel.
- LayoutTree terminaux (cellules/LeafView, layout.json via ProjectStore) = domaine DISTINCT ; ne pas confondre avec le placement des vues.
- `TicketListQuery` (ports/index.ts:717) N'A PAS de sort/order. Pagination RÉELLE (useTicketSearch + loadMore, TicketsPanel.tsx:225 charge 100 puis +100).
## #21 tri tickets (low) — NON frontend-pur : B + F
Le tri est une préoccupation de QUERY, symétrique du filtrage déjà backend-driven (TicketGateway.list→ticket_list). Comme la liste est paginée, trier client ne trie que les pages chargées → FAUX. Refuser le « frontend-pur ».
- **B** : étendre `TicketListQuery`/`ticket_list` avec `sort?:{field:"number"|"priority"|"status"|"title"; direction:"asc"|"desc"}`. ORDRE TOTAL déterministe obligatoire (tie-break par number croissant, sinon curseur casse). priority/status = ordre sémantique enum explicite, pas alpha. Défaut absent = comportement actuel (champ optionnel, rétro-compat). Companion recommandé non imposé : solder la dette basse curseur offset non opaque/stable (cf. [[ui-rework-sprint-scoping-contracts]]) — peut rester ticket séparé.
- **F** : contrôle « Trier par… » dans `TicketFacetsBar` (partagé TicketsPanel + TicketPicker #18). `useTicketSearch` relaie `sort` dans la query, `sort` dans la queryKey (reset pagination). Ne pas toucher le traitement OPAQUE du curseur (G3-bis). Groupement-par-sprint reste client ; tri s'applique intra-groupe.
- Dépend #16/#18 (livrés). Indépendant de #22/#23.
## #22 Anchor/docking views (medium) — frontend-pur V1 ; persistance = B optionnel différé
NE PAS réutiliser FloatingWindow (overlay modal) ni LayoutTree terminaux. NOUVELLE primitive de docking chrome-level, en-flux (pas overlay).
- Frontière : docking = état présentation chrome-level machine-local, distinct du domaine LayoutTree (réservé cellules PTY). Jamais dans LayoutGrid/LeafView (G5).
- Généraliser `openPanel:PanelId|null` en modèle de placement : `ViewPlacement = "closed"|"floating"|{dock:"left"|"right"}` par PanelId — conçu EXTENSIBLE pour #23 (ajoutera "detached"). Invariant : une vue = exactement un slot. V1 reco : 1 vue ancrée par côté, largeur redimensionnable.
- **F** : `shared/ui/DockRegion` (colonne verticale resizable gauche/droite du <main>), header titre + bascule flottant↔ancré. ProjectsView remplace openPanel par le placement. Réutilise composants de vue existants tels quels.
- **B optionnel différé** : persistance restore-au-redémarrage = préférence UI machine-local → store global settings.json/workspace.json (archi §9.2 via ProjectStore), PAS layout.json projet. V1 éphémère (useState) ; persistance = follow-up ticket dédié.
- Autonome. FOURNIT le modèle de placement dont #23 dépend.
## #23 views en fenêtres système (medium) — impact backend Rust + composition root
Cas spécialisé du chantier L10 multi-fenêtre (Tauri v2 WebviewWindow, protocole detach, archi §13.3). Avantage vs detach onglet-terminal : vues gateway/read-model-driven, PAS liées à un Channel PTY → nouvelle WebviewWindow ré-instancie le DI et re-souscrit aux gateways, AUCUN transfert d'état lourd.
- **B (app-tauri)** : command `open_view_window({panel,projectId})` créant une WebviewWindow sur entrée dédiée (URL param ?panel=&project=), lifecycle (focus/close/registre anti-doublon), events cycle de vie (fermeture OS → re-bascule placement). Composition root (di.tsx) : supporter un shell « panneau seul » (mêmes adapters/gateways).
- **F** : nouveau port `WindowGateway.openViewWindow(panel,projectId)` (+ closeViewWindow) avec adapter Tauri + mock (jamais invoke direct). Entrée panel-only lisant panel/project depuis URL. Menu View : action « Ouvrir dans une nouvelle fenêtre ».
- Articulation #22 FIGÉE : `ViewPlacement = "closed"|"floating"|{dock:"left"|"right"}|"detached"`. Une vue = soit ancrée soit flottante soit détachée, jamais deux (même invariant un-seul-slot). #23 = ajoute "detached" + plomberie Tauri.
- DÉPEND du modèle de placement de #22.
## Ordre & dépendances
- #21 en parallèle, autonome (petit B+F, le plus rapide, chaîne tickets uniquement).
- #22 AVANT #23 : #22 pose ViewPlacement que #23 étend. #23 d'abord = réinventer puis retravailler.
- Frontières : #21 seul à toucher domaine tickets (query sort) ; #22 présentation pure (docking ≠ LayoutTree) ; #23 seul à toucher app-tauri/composition root + nouveau port WindowGateway.

View File

@ -0,0 +1,44 @@
---
name: web-client-is-single-column-no-desktop-shell
description: memory note web-client-is-single-column-no-desktop-shell
metadata:
type: project
---
# Le client web n'a pas de shell desktop (ni docks, ni layout grid)
Constat établi en livrant le ticket #69 (adaptibilité téléphone), sur `develop @ 506d589`.
## Le fait
Le client web livré par #13 (`VITE_TRANSPORT=http`) **n'a jamais eu** de docks
gauche/droite, de fenêtres flottantes, de `LayoutTree`, de tabs projet, de menus ni
de toasts. `main.tsx` route `isWeb` vers `WebApp`, qui est une surface autonome :
- `WebApp``PairingScreen` **ou** `WebWorkspace` (rien d'autre) ;
- `WebWorkspace` → une **colonne verticale unique** (`flex flex-col overflow-y-auto`) ;
- `WebAgentCell``TerminalView`.
Les seuls imports `@/features/*` de tout `features/web/` sont `terminals` et
`workstate/useProjectWorkState`. Le shell desktop (`App`, layout, docks) n'est
atteignable que par la branche non-web de `main.tsx`.
## Pourquoi ça compte
Le cadrage de #69 prévoyait un lot « remplacer les docks gauche/droite/floating par
une pile verticale, drawer ou bottom sheet ». Ce lot était **sans objet** : il n'y a
pas de dock à remplacer, et l'arbitrage produit « bottom sheet vs drawer » ne se pose
pas sur cette surface. Le vrai travail téléphone était ailleurs (terminal xterm
pilotable au doigt, `dvh`, safe-area, rangées qui débordent à 360px).
**Avant de cadrer un ticket « responsive / mobile » sur le client web, partir de
`features/web/`, pas du shell desktop.** Les deux surfaces ne partagent que
`TerminalView` et les hooks transport-neutres.
## Garde en place
`frontend/src/features/web/WebMobile.test.tsx` épingle la frontière : le client web
ne doit monter aucun `layout-grid` / `layout-grid-container` / `layout-split` /
`layout-tabs`. Si un jour on veut du desktop-shell dans le web, ce test tombe — et
c'est le signal qu'il faut re-cadrer, pas le supprimer.
Voir aussi [[ui-rework-sprint-scoping-contracts]].

View File

@ -0,0 +1,15 @@
---
name: workstate-background-tasks-projection-fix
description: Décision d'archi pour rendre visibles Cancel/Retry dans le panneau Work — extension du read-model GetProjectWorkState, contrat DTO et borne de livraison.
metadata:
type: reference
---
**Défaut** : `AgentWorkState` (`crates/application/src/workstate/mod.rs:140`) ne portait pas les tâches de fond et `GetProjectWorkState` n'avait pas de dépendance `BackgroundTaskStore`. Le panneau Work masque la section (`ProjectWorkStatePanel.tsx:638`, `agent.backgroundTasks.length > 0`) → aucun bouton Cancel/Retry en live. Le frontend était déjà entièrement câblé (`normalizeAgent`/`normalizeBackgroundTask`), il ne manquait que la projection backend.
**Décision (frontière)** : étendre le read-model unique — ajouter `background_tasks: Vec<AgentBackgroundTaskState>` à `AgentWorkState` et brancher `BackgroundTaskStore` via un builder best-effort `with_background_tasks(store)` (calqué sur `with_conversation_sources`), `None` ⇒ zéro régression. **Pas** de fetch séparé côté front (garderait la jointure tâche→agent hors du domaine, casserait l'instantané cohérent, dupliquerait les cycles async). Aucun nouveau port/adapter. `list_background_tasks` (B7) reste comme read-model autonome, hors chemin du panneau.
**Contrat** : `AgentBackgroundTaskState` = miroir de `BackgroundTaskDto` (`dto.rs:2886`) sans owner/project. Projection dans `execute` = union `list_open_for_agent(agent.id)` (per-agent, running/queued/waiting) + `list_undelivered_completions()` (chargé UNE fois avant la boucle, dispatché par `owner_agent_id`, pour failed/cancelled → Retry). Best-effort strict : erreur store ⇒ `Vec::new()` pour l'agent, jamais d'`AppError` (live/busy/tickets intacts).
**Borne V1 assumée** : une tâche terminale **déjà livrée** (wake tiré, `completion_delivered=true`) n'est plus énumérable (droppée des deux listes) → disparaît du panneau ; Retry seulement dans la fenêtre non-livrée. Conforme à la note « retry session-scoped V1 » de `RetryBackgroundTask` (`background/mod.rs:212`). Historique Retry persistant = V2 (ajouterait `list_recent_terminal_for_agent` au port), ticketable si besoin.
**Wiring** : `.with_background_tasks(state.background_task_store.clone())` dans `crates/app-tauri/src/state.rs` (ancre `b7-wiring-anchor-state-rs`) ; le store y est déjà managé (utilisé par `list_background_tasks`). Lié à [[b8-in-app-trigger-run-in-background]], [[b8-command-runner-pty-framing]], [[background-tasks-first-class-design]], [[checkpoint-t1-wake-delivery-bug-fixed-rebuild-pending]].

View File

@ -149,6 +149,44 @@
],
"fallback": "allow"
}
},
{
"agentId": "c3e0d9d8-df36-4b00-b271-62d8aa78892c",
"permissions": {
"rules": [
{
"capability": "read",
"effect": "allow",
"paths": [
"**"
],
"commands": []
},
{
"capability": "write",
"effect": "deny",
"paths": [
"**"
],
"commands": []
},
{
"capability": "delete",
"effect": "deny",
"paths": [
"**"
],
"commands": []
},
{
"capability": "executeBash",
"effect": "allow",
"paths": [],
"commands": []
}
],
"fallback": "allow"
}
}
]
}

View File

@ -0,0 +1,11 @@
---
id: "028179b1-eaf4-41e9-9c1f-7c37125117e6"
order: 2
name: "Client - serveur"
status: "planned"
createdBy: {"kind":"user"}
updatedBy: {"kind":"user"}
createdAt: 1783329381447
updatedAt: 1783330421737
version: 4
---

View File

@ -0,0 +1,11 @@
---
id: "5afd6780-0f76-40d7-a10f-32ee52469d74"
order: 1
name: "UI rework"
status: "planned"
createdBy: {"kind":"user"}
updatedBy: {"kind":"user"}
createdAt: 1783329530306
updatedAt: 1783330421736
version: 3
---

View File

@ -0,0 +1,11 @@
---
id: "883534aa-7fc1-4d83-a9c0-17cac4a4eea5"
order: 4
name: "Modeles locaux"
status: "planned"
createdBy: {"kind":"user"}
updatedBy: {"kind":"user"}
createdAt: 1783757092388
updatedAt: 1783757205620
version: 2
---

View File

@ -0,0 +1,11 @@
---
id: "d8f3f37b-87ca-4509-9116-45a99bc711df"
order: 3
name: "Session limites handle"
status: "planned"
createdBy: {"kind":"user"}
updatedBy: {"kind":"user"}
createdAt: 1783329454681
updatedAt: 1783330421738
version: 3
---

View File

@ -0,0 +1,11 @@
---
id: "e28a4d53-8bd2-446a-b0ac-2a017373b8b2"
order: 5
name: "Gestion des bugs"
status: "planned"
createdBy: {"kind":"user"}
updatedBy: {"kind":"user"}
createdAt: 1783923775150
updatedAt: 1783923775150
version: 1
---

50
.ideai/sprints/index.json Normal file
View File

@ -0,0 +1,50 @@
{
"version": 1,
"sprints": [
{
"id": "5afd6780-0f76-40d7-a10f-32ee52469d74",
"path": "5afd6780-0f76-40d7-a10f-32ee52469d74",
"order": 1,
"name": "UI rework",
"status": "planned",
"updatedAt": 1783330421736,
"version": 3
},
{
"id": "028179b1-eaf4-41e9-9c1f-7c37125117e6",
"path": "028179b1-eaf4-41e9-9c1f-7c37125117e6",
"order": 2,
"name": "Client - serveur",
"status": "planned",
"updatedAt": 1783330421737,
"version": 4
},
{
"id": "d8f3f37b-87ca-4509-9116-45a99bc711df",
"path": "d8f3f37b-87ca-4509-9116-45a99bc711df",
"order": 3,
"name": "Session limites handle",
"status": "planned",
"updatedAt": 1783330421738,
"version": 3
},
{
"id": "883534aa-7fc1-4d83-a9c0-17cac4a4eea5",
"path": "883534aa-7fc1-4d83-a9c0-17cac4a4eea5",
"order": 4,
"name": "Modeles locaux",
"status": "planned",
"updatedAt": 1783757205620,
"version": 2
},
{
"id": "e28a4d53-8bd2-446a-b0ac-2a017373b8b2",
"path": "e28a4d53-8bd2-446a-b0ac-2a017373b8b2",
"order": 5,
"name": "Gestion des bugs",
"status": "planned",
"updatedAt": 1783923775150,
"version": 1
}
]
}

View File

@ -0,0 +1,29 @@
---
issueRef: "#1"
version: 11
updatedBy: {"kind":"user"}
updatedAt: 1783175265022
---
- Cadrage: background-tasks-first-class-design. Arbitrage 5 écarts: b8-arbitration-outcomes.
- FAIT B8 runner PTY + boucle sink fermée + refactor point-2. QA vert. Commit backend 8cac147.
- FAIT UI cancel/retry câblés (cancel_background_task/retry_background_task). Commit frontend c179f93.
- FAIT déclencheur in-app: surface MCP idea_run_in_background (BE-1+BE-2), commit f08dae6. Ferme l'écart de périmètre « aucun producteur ».
- Dette tracée: #2 (PtyPort::wait + tee live), #3 (persistance sûre invocation/retry-after-reboot + énumération store), #5 (contrat DTO work-state).
## MAJ 2026-07-04 — part autonome de #1 clôturée (Main)
- AppImage rebuild frais confirmé (build 2026-07-03 23:56 > dernier source 23:53), inclut T1+T3, NO_STRIP=true.
- VALIDATION AUTOMATISÉE VERTE : cargo build --workspace OK ; cargo test -p application / -p app-tauri / -p infrastructure = 0 failed ; frontend npm run build OK + vitest 459/460. L'unique rouge = flake de timing src/features/permissions/permissions.test.tsx (passe en isolation 2/2, non touché par #1, pas une régression).
- COMMITS Git sur feature/background-tasks-first-class : eb9cc16 (code T1+T3, 21 fichiers) + ebd992e (19 notes mémoire + MEMORY.md). AUCUN merge vers develop. Runtime .ideai/{background-tasks,proposals,tickets}/ volontairement non commités.
## ⚠️ MAJ 2026-07-04 — IMPACT TOPOLOGIE (constat Git, cadrage #4)
- `feature/background-tasks-first-class` (#1) est DÉRIVÉE du `develop` ANORMAL/rembobiné (merge-base #1↔main = 1fc7869, pré-canonique ; a9653bc tip develop est ancêtre de #1). Donc #1 = baseline PRÉ-CANONIQUE (spawn_turn=0, run_turn batch, pas d'AgentTurnEvent).
- La vraie ligne d'intégration est `main` (canonique, release ~01/07), PAS le develop rembobiné (18 commits en retard sur main). ⇒ la cible de merge « → develop » prévue au point 3 ci-dessous est INVALIDÉE.
- #1 modifie massivement les fichiers divergés côté canonique : orchestrator/service.rs +425, domain/events.rs +184, lifecycle.rs +83, session/codex.rs. ⇒ portage sur canonique = CONFLITS NON TRIVIAUX attendus, pas un simple changement de cible.
- PLAN Git (Phase C, à faire quand on reprendra #1) : branche fraîche `feature/background-tasks-first-class-canonical` depuis main + cherry-pick séquentiel des 17 commits (ancienne branche gardée comme filet), conflits sémantiques service/events escaladés à DevBackend/Architect, puis QA avant tout merge.
- Décision de reconstruction de `develop` sur `main` : DESTRUCTIVE (rewrite branche partagée) → en attente validation utilisateur.
## RESTE — DÉPENDANT UTILISATEUR (Main ne peut pas seul)
1. Re-QA LIVE T3 : relancer l'AppImage fraîche puis vérifier onglet Work → Cancel/Retry (T3-a..f du cadrage workstate-background-tasks-projection-fix). QA non pilotable depuis sandbox → besoin app lancée.
2. T2 — reconcile après reboot (fin AVANT wake), protocole MANUEL, coordination reboot utilisateur.
3. [CIBLE À REVOIR] merge de #1 : ne plus viser le develop rembobiné. Porter #1 sur canonique (Phase C ci-dessus) PUIS merger vers develop-reconstruit/main. Dépend de la décision de reconstruction de develop.
Main travaille sur #4 (annonces inter-agent) en attendant la dispo utilisateur pour 1-2-3.

24
.ideai/tickets/1/issue.md Normal file
View File

@ -0,0 +1,24 @@
---
id: "ec19c041-7a44-456d-994d-f0dcb7c52f39"
number: 1
title: "Tâches de fond — finaliser B8 (runner PTY) et clôturer le chantier"
status: "closed"
priority: "medium"
links: []
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
createdBy: {"kind":"user"}
updatedBy: {"kind":"user"}
createdAt: 1783026129089
updatedAt: 1783175265022
version: 11
---
Description :
Le chantier « tâches de fond de 1re classe » est livré et QA-vert jusqu'à B7 inclus (mailbox bornée, rendez-vous inter-agent comme tâche durable, reconcile au boot, réveil du propriétaire, UI F1F4). Commité sur feature/background-tasks-first-class (e05edc6 backend, 5d88c95 frontend), non mergé. Reste à fermer les points suivants avant de considérer le chantier terminé.
Reste à faire :
- [ ] B8 — runner de commandes couplé PTY : créer un BackgroundTask{kind: Command} au spawn d'une commande longue (côté pty.rs / LocalProcessSpawner) et pousser exit/stdout/stderr dans le completion sink. C'est ce qui active le flux run_in_background complet : une commande qui finit après la fin du tour de l'agent réveille automatiquement son propriétaire avec le résultat. Aujourd'hui le sink est prêt mais aucun runner concret ne s'y abonne.
- [ ] UI cancel / retry des tâches de fond : les boutons existent mais sont désactivés faute de commande Tauri. Exposer cancel/retry (dépend de B8) et brancher l'UI.
- [ ] Validation live end-to-end de la feature dans l'app réelle : lancer un build/commande longue en run_in_background, laisser le tour se terminer, vérifier que le propriétaire est bien re-réveillé avec le résultat (et idem après redémarrage d'IdeA entre la fin et le wake — le reconcile doit retrouver la complétion).
- [ ] Merge feature/background-tasks-first-class → develop (sur validation) : résorbe aussi les 4 tests de protocole MCP périmés qui sont encore rouges sur develop (le fix d'alignement vit sur cette branche).

View File

@ -0,0 +1,6 @@
---
issueRef: "#10"
version: 5
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedAt: 1783241883970
---

View File

@ -0,0 +1,15 @@
---
id: "37bfbb89-a8a5-475f-91bf-ecfd60ee4203"
number: 10
title: "Ticket par sprint"
status: "closed"
priority: "medium"
links: []
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
createdBy: {"kind":"user"}
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
createdAt: 1783183841014
updatedAt: 1783241883970
version: 5
---
J'aimerais pouvoir regrouper mes tickets en sprints. C'est a dire en catégories qui auraient elles meme un ordre d'execution (sprint 1, sprint 2 etc)

View File

@ -0,0 +1,6 @@
---
issueRef: "#11"
version: 6
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedAt: 1783278726067
---

View File

@ -0,0 +1,15 @@
---
id: "dad53cb9-a818-4475-be12-fd3ba6c59638"
number: 11
title: "Interface de création de sprint"
status: "closed"
priority: "medium"
links: [{"target":"#10","kind":"dependsOn"}]
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
createdBy: {"kind":"user"}
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
createdAt: 1783183911244
updatedAt: 1783278726067
version: 6
---
Pour créer des sprints, j'aimerais une inteface de création dans laquelle je pourrais facilement ajouter/enlever des tickets de mes différents sprint

View File

@ -0,0 +1,6 @@
---
issueRef: "#12"
version: 5
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedAt: 1783312900907
---

View File

@ -0,0 +1,15 @@
---
id: "fb8a73f9-c871-4513-aa09-b35fbbef637a"
number: 12
title: "Checkbox pour les filters des tickets"
status: "closed"
priority: "low"
links: []
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
createdBy: {"kind":"user"}
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
createdAt: 1783184054842
updatedAt: 1783312900907
version: 5
---
Pour les filtres des tickets, j'aimerais qu'on ai une checkbox plutot que la selection d'un seul filtre, de façon a pouvoir faire des combinaisons de plusieurs priorité et de plusieurs status par exemple

View File

@ -0,0 +1,54 @@
---
issueRef: "#13"
version: 19
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedAt: 1784193634690
---
# Ticket #13 — Server/client mode (carnet de chantier)
Ticket gated : review agent-utilisateur faite 2026-07-15. Base develop. Aucune action sortante.
## Arbitrages produit validés (utilisateur)
- Remote perso, 1 SEUL user. Pas de multi-tenant. CLI natif via WS PTY, xterm.js inchangé, PTY serveur. SSH écarté.
- Desktop Tauri ET client/serveur sur cœur backend commun. Exposition Internet ⇒ TLS/reverse proxy obligatoire.
- Packaging `idea --serve`, artefact unique. Auth pairing code + cookie session, jamais dans l'URL.
- Multi-fenêtres OS HORS V1 web. B6→B8 + servir dist, validation live groupée à la fin.
## Contrat de transport figé (Architect)
- `POST /api/invoke {command,args}` (allowlist read-only). Cookie session `HttpOnly,Secure,SameSite=Strict` au pairing (`POST /api/pair`). `POST /api/logout` = révocation (B8). Cookie invalide→401 ; mauvais code→403 ; Origin refusée→403.
- Frames WS JSON base64, ack unifié `terminal.attached`, output APRÈS l'ack. structured→UNSUPPORTED.
- Flags : `--listen`, `--public-origin`, `--allow-remote`, `--trust-reverse-proxy`, `--app-data-dir`, `--web-root` (B8).
## Plan de lots — TOUS LES LOTS DEV LIVRÉS ✅
B0→B8 + F1→F6 livrés/verts/committés. + correctifs live écarts 1/2. Reste : finir la validation live groupée (utilisateur) puis merge develop + clôture.
## Livrés VERT
- develop (e457152) : B0 5505acc · B1 955db79 · B2 c8fef2a · F1 e0cdb4a · B3 4ed0b16 · B4 fa353f6 · F2 e500e31.
- feature/ticket13-pty-websocket : B5 917be99 · F3 7487902 · B6 6391d1c · F4 5254f16 · B7 1bc5217 · F5 dd1d083 · B8 d538808 · F6 6117177 · **fix live écarts 1+2 c9ce3d7**.
- B8 : sert dist/ same-origin, serve_static durci, `/api/logout`, logs sécurité, doc remote. 57/57.
- F6 : logout, 401→pairing, bannière reconnexion, serveur indispo. build+vitest 758/758.
## ⚙️ Validation live 1er passage (utilisateur) — 3 écarts remontés
1. **Erreur `__TAURI_INTERNALS__ undefined` + projets vides** = MA commande de test était incomplète (build sans `VITE_TRANSPORT=http` → build desktop dans le navigateur ; et `--app-data-dir` non pointé sur le dossier desktop). PAS un bug de code.
2. **Défaut app-data-dir désaligné** (RÉSOLU c9ce3d7) : `default_app_data_dir()` retournait `~/.local/share/IdeA` alors que le desktop = identifier tauri `app.idea.ide` (`~/.local/share/app.idea.ide`). Corrigé : précédence `--app-data-dir` > `IDEA_APP_DATA_DIR` > `$XDG_DATA_HOME/app.idea.ide` > `$HOME/.local/share/app.idea.ide` + log démarrage `idea --serve: app data dir = <path>`. Doc corrigée (npm + VITE_TRANSPORT=http + avertissement double-writer). Garde-fou lock inter-process app-data-dir = ticket court à créer AVANT d'ouvrir des écritures web.
3. **Bouton Browse (créer/ajouter projet) inopérant en web** = `pickFolder()``UNSUPPORTED_ON_WEB` (dialogue natif OS absent du navigateur). PAS régression : report documenté F1. Sélection d'un projet EXISTANT couverte par list_projects/open_project (OK une fois écart 2 corrigé). Créer un NOUVEAU projet en web = nouveau lot (folder browser serveur sandboxé + create_project write) → **ticket #64 créé** (dependsOn #13), cadré par Architect, hors #13.
## ⚠️ RESTE : re-passage validation live (utilisateur, HORS SANDBOX)
Commande corrigée :
1. `cd frontend && VITE_TRANSPORT=http npx vite build` (npm, jamais pnpm — voir mémoire `frontend-uses-npm-not-pnpm`).
2. Fermer l'app desktop, puis `cargo run --release -p app-tauri -- --serve --web-root frontend/dist --app-data-dir ~/.local/share/app.idea.ide` (pairing code + app data dir sur stderr).
3. Navigateur `http://127.0.0.1:17373` → écran pairing → code.
4. Vérifier : liste projets (existants visibles), ouvrir projet, terminal CLI (frappe→serveur, reload→réattache/scrollback), cellule agent, surfaces live (workstate/background Cancel-Retry/inbox), reconnexion, logout→pairing.
## Réserves / écarts V1 (acceptés)
- Replay V1 = repaint scrollback complet. Multi-onglets « dernier gagne » silencieux. F4 write-portal NON câblé web. Browse créer-projet web → #64. Lock double-writer app-data-dir → ticket court à créer.
## Branches
`feature/ticket13-pty-websocket` (courante). À merger dans develop après validation live OK.
## Prochaine étape
Re-passage validation live (commande corrigée ci-dessus). Si OK → Git merge dans develop → clôture #13. #64 (Browse web) à planifier après. Aucune action sortante sans validation utilisateur.
## Dette hors chantier
Warnings clippy pré-existants crates/domain (fileguard/profile/sprint).

View File

@ -0,0 +1,16 @@
---
id: "036783fa-9856-4643-8c28-653b93ac36e1"
number: 13
title: "Server/client mode"
status: "closed"
priority: "low"
sprint: "028179b1-eaf4-41e9-9c1f-7c37125117e6"
links: []
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
createdBy: {"kind":"user"}
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
createdAt: 1783184188272
updatedAt: 1784193634690
version: 19
---
J'aimerais pouvoir utiliser IdeA sous forme de client/server. C'est a dire que IdeA aurait son backend sur une machine et son interface qui serait un frontend web. Les agents etc seraient tous côté server. C'est a dire que la cli de Claude serait celle côté serveur, l'interface client ne serait que l'affichage frontend, une UI. Je pense que tout est faisable, il faudrait cependant parler du côté CLI. Est ce qu'il serait possible de garde rle CLI natif comme il est actuellement, de l'afficher côté client tout en gardant l'installation claude code, codex etc côté serveur ? Peut etre qu'en ssh ça passerait ? Ce ticket ne pourra pas être fait en autonomie par un agent sans une review agent-utilisateur avant.

View File

@ -0,0 +1,20 @@
---
issueRef: "#14"
version: 7
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedAt: 1783456579694
---
## Production #14 — livrée dans develop (validation e2e live restante)
Périmètre figé avec l'utilisateur : adapter HTTP OpenAI-compatible **purement additif** (zéro régression Claude/Codex), parité **tool-calling + MCP** complète, canal HTTP natif robuste.
### Cycle
- Cadrage Architect : slug mémoire `ticket14-local-lan-openai-adapter-scoping` (asymétrie CLI-vs-serveur HTTP, port ToolInvoker in-process, reprise sans id provider).
- Backend (DevBackend) : variante `StructuredAdapter::OpenAiCompatible`, VO `HttpChatConfig`, `openai_compat.rs`, port `ToolInvoker` déléguant à la même OrchestratorService que le MCP. GO QA (domain 467 / application 530 / infrastructure 523 / app-tauri 247, 0 échec). Défaut réel corrigé : timeout post-contact mappé Io.
- Frontend (DevFrontend) : types DTO miroir, wizard/settings (endpoint/model/apiKeyEnv=nom de var/timeouts, validation miroir backend), profil de référence Ollama éditable, erreur endpoint rendue via bannière role="alert". GO QA (tsc 0 + vitest 59 fichiers / 566 tests, 0 échec). Défaut réel corrigé : erreur endpoint invisible dans le DOM.
### Git
- Backend `aab4bca`, frontend `d89380c`, merge `--no-ff` `3c6cd04` → develop. Branche feature supprimée. **Local uniquement, rien poussé.**
### Reste à faire (hors code)
- Validation e2e **live contre un vrai endpoint Ollama/LAN** : lancement cellule, conversation headless, reprise, délégation inter-agents, vivacité/timeout, endpoint indisponible/modèle absent (critère d'acceptation n°7). Non exercée en tests automatisés — c'est la raison du statut QA plutôt que closed.

View File

@ -0,0 +1,75 @@
---
id: "0c1f1991-b6db-47b7-bbef-2478bc7874ab"
number: 14
title: "Integration de profils IA locaux/LAN comme profils IdeA canoniques"
status: "qa"
priority: "medium"
sprint: null
links: []
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
createdBy: {"kind":"user"}
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
createdAt: 1783184495846
updatedAt: 1783456579694
version: 7
---
Objectif : permettre à IdeA d'utiliser des modèles hébergés localement ou sur le réseau local, par exemple Qwen via Ollama ou un runtime Docker exposant une API, avec le même niveau d'intégration produit que les profils Claude Code et OpenAI Codex CLI.
La finalité n'est pas un simple profil custom lancé dans un terminal brut. Ces profils doivent devenir de vrais profils IA IdeA : sélectionnables, lançables depuis les cellules, pilotés par la conversation headless canonique, compatibles avec les mécanismes de conversation/reprise, et utilisables par l'orchestration de la même manière que Claude/Codex.
## Cible produit
Ajouter une famille de profil structuré pour serveurs de modèles locaux ou LAN, idéalement basée sur une API HTTP compatible OpenAI/Ollama :
- endpoint configurable (`localhost`, machine LAN, container Docker, etc.) ;
- modèle configurable (`qwen...`, autre modèle local) ;
- authentification optionnelle via nom de variable d'environnement, jamais clé en clair ;
- profil visible et sélectionnable dans IdeA comme Claude/Codex ;
- cellule IdeA utilisable de façon canonique : conversation headless, historique, reprise, délégation, affichage des réponses et état de vivacité.
## Contraintes importantes
- Ne pas considérer le mode PTY/TUI comme suffisant pour ce ticket.
- Éviter une dépendance obligatoire à `ollama`, `docker` ou une commande shell quand une API HTTP suffit.
- Étudier explicitement les impacts commandes/headless :
- adapter HTTP natif ;
- wrapper CLI éventuel ;
- Docker/LAN ;
- sandbox et permissions ;
- absence ou présence de tool-calling/MCP côté modèle local.
- Le résultat doit s'intégrer dans le modèle existant `StructuredAdapter` / `AgentSessionFactory`, pas contourner le runtime IA d'IdeA.
## Travail attendu
1. Ajouter un adapter structuré pour modèle local/LAN, par exemple `StructuredAdapter::OpenAiCompatible` ou `LocalChat`.
2. Étendre le modèle de profil pour porter la configuration nécessaire : endpoint, model, apiKeyEnv optionnel, timeouts/liveness si nécessaire.
3. Implémenter une session headless `AgentSession` :
- `send(prompt)` ;
- émission de `ReplyEvent` ;
- gestion propre des erreurs réseau/modèle ;
- conversation id ou stratégie de reprise compatible avec le modèle IdeA.
4. Brancher l'adapter dans `StructuredSessionFactory`.
5. Rendre ces profils sélectionnables dans le wizard/settings au même titre que Claude/Codex.
6. Ajouter un profil de référence local, par exemple “Ollama / OpenAI-compatible local model”, éditable.
7. Vérifier l'intégration avec :
- lancement depuis une cellule ;
- conversation headless ;
- reprise ;
- changement de profil ;
- délégation inter-agents quand applicable ;
- état de vivacité/timeout ;
- erreurs endpoint indisponible/modèle absent.
## Critères d'acceptation
- Je peux configurer un profil Qwen hébergé via Ollama ou endpoint LAN.
- Je peux créer/lancer un agent IdeA avec ce profil comme avec Claude/Codex.
- La cellule utilise le chemin headless structuré, pas un terminal brut PTY.
- Les conversations apparaissent et se comportent comme les conversations Claude/Codex.
- La reprise ne mélange pas l'id logique IdeA avec un éventuel id provider.
- Un endpoint indisponible produit une erreur propre dans l'UI, sans bloquer IdeA.
- Les dépendances à `ollama`, Docker ou une CLI externe sont documentées et non imposées si le mode HTTP suffit.
- Tests backend couvrant factory, session adapter, erreurs HTTP et sérialisation du profil.
- Tests frontend couvrant configuration/sélection du nouveau profil.
Review agent-user requise avant démarrage d'implémentation pour figer le périmètre exact : HTTP natif uniquement, support d'un wrapper CLI, et niveau attendu de tool-calling/MCP pour les modèles locaux.

View File

@ -0,0 +1,15 @@
---
issueRef: "#15"
version: 5
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedAt: 1784093861343
---
## Cadrage Architect — DIFFÉRÉ (2026-07-13)
#15 (cas DÉLÉGUÉ : A→B via idea_ask_agent, B limité → parquer le waiter de A et re-livrer au reset) est **distinct de #30** (cas direct) et **différé hors sprint**.
**Motif (Architect)** : le faire maintenant en in-memory serait une rustine fragile — réintroduit un blocage long côté A et ne tient pas les scénarios reboot/crash/redémarrage IdeA déjà identifiés.
**Prérequis bloquant** : un modèle fiable de *délégation durable runtime-agent identity* (stockage waiter, corrélation requester/target/request, re-livraison idempotente, reprise après reset, comportement si A meurt / B redémarre / IdeA redémarre, expiration/annulation). → design `durable-delegation-runtime-agent-identity-design`.
**Statut** : reste ouvert, priorité low/stretch conservée. Dépendance baseline #7 maintenue + à rattacher au redesign délégation durable. Ne pas mélanger avec #30.

View File

@ -0,0 +1,28 @@
---
id: "5de121f3-1cd1-4a73-bcab-b0a43a7c257f"
number: 15
title: "[Bloqué par #7 — design durable] Limites de session — re-livraison auto parquée au reset (stretch B5)"
status: "open"
priority: "low"
sprint: "e28a4d53-8bd2-446a-b0ac-2a017373b8b2"
links: [{"target":"#7","kind":"dependsOn"}]
agentRefs: []
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
createdAt: 1783208599385
updatedAt: 1784093861343
version: 5
---
Extrait du cadrage Architect du ticket #7 (baseline livrée : propagation inter-agent B1→B3 + F1/F2). Stretch non retenu dans #7.
⛔ STATUT (2026-07-15) : BLOQUÉ / PARQUÉ. Dépend de #7 qui est encore en QA (non stabilisé). Architect (triage sprint « Gestion des bugs ») : « le waiter A→B n'est pas parqué/re-livré, l'ask retourne encore une erreur Process ; à traiter APRÈS stabilisation QA de #7/LS7 ». De plus c'est un item de « délégation durable » (in-memory fragile aux redémarrages) qui relève d'un design dédié, pas d'une rustine. Non lançable tant que #7 n'est pas vert et que le design durable-delegation n'est pas cadré. Sorti de la production active du sprint.
--- Cadrage conservé ---
Objectif : quand A délègue à B via idea_ask_agent et que B atteint sa limite, au lieu de demander à A de ré-interroger après le reset, PARQUER le waiter de A par (requester,target) ; au execute_resume de B (reprise auto au reset), RE-LIVRER la tâche d'origine et router la complétion vers le waiter de A s'il est encore vivant (sinon drop, contrainte in-memory).
État actuel (triage) : le rendez-vous délégué détecte RateLimited et arme une reprise de la cible (crates/application/src/orchestrator/service.rs:1854), execute_resume relance l'agent (crates/application/src/agent/session_limit.rs:226) ; mais le waiter A→B n'est pas parqué/re-livré.
Coût/risque (Architect) : rapproche du modèle « délégation durable » tout en restant in-memory → fragile aux redémarrages/interruptions de A, ré-introduit un blocage long côté A. À rattacher au design durable-delegation-runtime-agent-identity-design plutôt que traité en rustine in-memory.
Dépend de : #7 (baseline inter-agent B1→B3). Voir mémoire ticket7-session-limit-interagent-cadrage.

Some files were not shown because too many files have changed in this diff Show More