Lot en un seul commit: les quatre tickets partagent les mêmes fichiers
(événements domaine/DTO, ProjectPluginsPanel, useProjectPlugins) — une
séparation par hunks aurait risqué des états intermédiaires non
compilables (convention lot des batches #310-#313, #315 et #325-#326).
#328 — section dédiée plugins dans Paramètres du projet:
- frontend: ProjectPluginsPanel intégré à ProjectSettingsPanel
(Panneaux > Paramètres du projet) — activation/désactivation par
plugin avec état actif/inactif explicite par projet, distinct de
l'assignation agents/skills/tools
- domaine: événement ProjectPluginSettingsChanged publié par
SetProjectPluginEnabled, MutateAgentPluginSkill et
MutateAgentPluginTool après persistance de .ideai/project-plugins.json
(application + backend + DTO camelCase, test dto.rs)
#329 — les menus plugin qui échouent ne sont plus muets:
- usePluginMenus: dispatcher strict (runCommandStrict) pour les
commandes réellement enregistrées au lieu du dispatcher laxiste qui
journalisait et avalait les rejets — rendait les échecs d'ouverture
de fenêtres natifs invisibles pour l'hôte de menus
- le no-op SDK historique (commande déclarée sans handler) est préservé
- échec remonté à l'hôte via commandError/clearCommandError (journal
console + état exposé); test window_usecases adapté à un plugin
installé
- le correctif côté SDK/GitPlugin vit dans le projet GitPlugin (son
commit/merge local y est fait, hors périmètre de ce dépôt)
#330 — refresh automatique des pages:
- frontend/domain: typage des événements pluginEnabled,
pluginDisabled (restartRequired), pluginUninstalled
(restartRequired), pluginLoadFailed et projectPluginSettingsChanged
- useProjectPlugins et PluginRuntimeProvider: souscription
onDomainEvent — refresh sur changement catalogue global ou sur
projectPluginSettingsChanged du projet courant; gardes
révision/projet contre les réponses obsolètes
- useProjectAgents: refresh auto quand un agent est ajouté au projet;
ProjectsView et AgentsPanel suivent (erreur de rafraîchissement
affichée avec bouton Réessayer)
#331 — droplist agents des cellules:
- SmallDropdown: liste bg-overlay + bordure border-strong (plus de
fond blanc hors thème), hover/focus bg-raised, sélection
bg-primary/15, disabled text-faint opacity-60, role=combobox
- LayoutGrid: sélecteur d'agent des cellules migré du <select> natif
vers SmallDropdown (mêmes libellés Terminal/visible
ailleurs/arrière-plan)
QA verte réelle sur les quatre lots (tests ciblés + exécution).
sdk/IdeaSDK: contenu modifié non committable ici (pointeur inchangé).
Aucun push.
La cellule expose un bouton Redémarrer l'agent valable pour les trois
modes de présentation (headless, TUI native, CLI custom), sans jamais
prendre la présentation pour vérité runtime.
- Application: use case RestartCellAgentRuntime validant le binding
exact cellule/runtime via les snapshots live (RestartRuntimeNotFound,
ConflictAgentVisibleElsewhere, AgentNotBoundToCell) puis déléguant au
scheduler self-restart existant (schedule_cell); CellAgentPresentation
purement informationnelle.
- Backend/app-tauri/web-server: commande restart_cell_agent sur contrat
DTO identique Tauri/HTTP, stop borné par timeout typé
(RestartStopTimeout), relance préservant routage TUI natif, géométrie
de cellule (node/rows/cols) et conversation; évenements domaine
enrichis.
- Frontend: bouton dans la barre de cellule avec confirmation,
overlay d'état Redémarrage…/échec (libellés par code d'erreur, Fermer/
Réessayer), gardes d'identité de cellule et de restarts externes,
gateway/mock/http adapters et ports alignés.
- Tests: dto_agents, workstate_actions, contrats web-server,
LayoutGrid.cellControlsLayering, adapters agent/mock/http.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
État d'intégration confiné à la branche batch. Les tickets #119 (skills →
capacités agent découvrables), #122 (override permissions par défaut), #131
(effort par agent/presets) et #132 (outil MCP d'édition du contexte projet)
sont verts en périmètre. Le sprint plugins multi-fichiers ESM / persistance
plugin-owned (#135/#136/#139) est co-implémenté dans les MÊMES fichiers de
câblage (frontend/src/ports/index.ts, backend/src/lib.rs, domain/ports.rs,
backend/dto.rs), inséparable sans staging interactif (indisponible ici).
Commit unique volontaire : préserve l'état vert QA sans découpe hunk risquée.
NON mergé vers develop tant que #137 (QA e2e plugins) n'est pas vert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Lots B1-B4 : modèle de domaine des plugins (manifeste, capacités menus/layouts/MCP),
port et registre applicatif, chargement/validation en infrastructure, exposition DTO
et commandes Tauri. Tests cargo verts.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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>
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>