Permet aux commandes Tauri des menus manquants (#90) d'être invoquées
depuis la version web via le proxy /api/invoke.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Repliage par défaut des background tasks par agent dans le WebWorkspace
(#87), fix frontend pur, tests verts (887 passants, tsc clean).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
WebWorkspace replie désormais par défaut la liste des background tasks
par agent dans le panneau Work ; QA a ajouté les cas de test associés
dans WebWorkspaceLive.test.tsx (887 tests verts, tsc --noEmit clean).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ticket #86, lot 2 : surface tickets/sprints pour le workspace web, avec fix
QA sur le changement/retrait de sprint sur un ticket. QA vert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
QA-flagged gap: the web surface could only ADD a ticket to a sprint, never
change or clear it, despite the backend already exposing both
ticket_assign_sprint and ticket_unassign_sprint.
- useTicketDetail: new setSprint(sprintId | null) method (additive, also
available to the desktop TicketDetail — unused there today, no behaviour
change), routing through the existing TicketGateway.setTicketSprint.
- WebTicketDetail: Sprint selector in "Statut et priorité", immediate save
like status/priority; empty value clears back to "Sans sprint".
- WebSprintsView: each sprint card now shows its tickets (compact list,
resolved client-side like the desktop SprintManager) with a "Retirer du
sprint" action per ticket, calling assignSprint(ref, null) — symmetric
with "Ajouter tickets". Extracted the card into a SprintCard subcomponent
to keep the growing card readable.
- Tests: WebTicketsSprints.test.tsx now has 16 tests (was 10) — added
sprint change/clear, sprint removal from the Sprints tab, agent
assign/unassign, ticket link/unlink, carnet save, and free-text search
filtering.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a Live/Tickets/Sprints tab navigation to WebWorkspace after a project
is opened — a mobile-first adaptation, not a port of the desktop docks/
floating windows, per carnet #86. The web-server transport (17 ticket_*/
sprint_* commands) and the HttpTicketGateway were already wired (lot 1 +
pre-existing gateway code); this lot is UI only.
- Tickets tab: search/filters, list grouped by sprint, create screen,
detail view with stacked accordion sections (Résumé/Statut et priorité/
Carnet open by default; Agents assignés/Liens/Zone dangereuse collapsed),
delete confirmation, optimistic-concurrency conflict banner.
- Sprints tab: create/rename/reorder (Monter/Descendre)/delete with
confirmation, add tickets via a mobile full-screen ticket picker (never
a small desktop modal), "Voir tickets" filters the Tickets tab to one
sprint.
- Reuses the transport-neutral hooks as-is (useTickets, useTicketDetail,
useTicketSearch) — only presentation and copy are web-specific, in
French per decision #78 (new local label module, not a reuse of the
English desktop ticketMeta labels).
- Workaround: `list_agents` isn't in the web-server allowlist, so agent
names/assignment options are derived from `get_project_work_state`
(already used by the Live tab) instead of the desktop's useProjectAgents.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ticket #86, lot 1 : commandes tickets/sprints exposées sur le transport web
(web-server), DTO tickets factorisés entre Tauri et web (ticket_dto.rs).
QA vert. Le lot 2 (frontend) suit sur une nouvelle branche.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Expose les commandes tickets/sprints sur le transport web (web-server/lib.rs),
avec factorisation des DTO tickets partagés entre Tauri et web dans un
nouveau module backend/src/ticket_dto.rs (app-tauri/src/tickets.rs
consomme désormais ce module commun).
Lot 1 du ticket #86, backend/web-server. Le lot 2 (frontend) suit sur une
nouvelle branche depuis develop à jour.
QA vert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ticket #83 : popup de confirmation à la fermeture d'IdeA si travail en
cours — guard backend (agents busy + tâches d'arrière-plan actives,
GetAppExitWorkGuardState) + popup frontend. QA vert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Expose l'état du guard de sortie applicative (GetAppExitWorkGuardState) :
agents busy + tâches d'arrière-plan actives à travers tous les projets
ouverts, avec détails compacts pour la popup de confirmation. Le handler
CloseRequested d'app-tauri interroge ce guard avant de laisser la fenêtre
se fermer, et respecte la confirmation explicite de l'utilisateur
(EXIT_GUARD_CONFIRMED) pour ne pas la redemander en boucle.
QA vert (backend + frontend).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Écoute l'event Tauri `app-exit-work-guard` (émis par le backend quand la
fermeture de la fenêtre main est interceptée) et affiche une popup modale
"Du travail est encore en cours" avec le résumé pluralisé exact du carnet
#83, le détail agents/tâches capé à 5 lignes, et les deux actions Annuler
(focus par défaut, no-op local) / Quitter quand même (danger, appelle
confirm_app_exit). Pas d'option "ne plus demander".
- domain/ports/adapters (Tauri listen+invoke, HTTP desktop-only stub, mock
avec helpers de test) : onAppExitWorkGuard/confirmAppExit sur
SystemGateway, suivant le patron déjà utilisé pour focused-project et les
domain events.
- AppExitConfirmDialog : mounted une fois près de la racine (App.tsx), à
côté d'AnnouncementsProvider — role="alertdialog", focus trap, Échap =
Annuler, pas de fermeture au clic extérieur, ne se referme jamais
automatiquement (un event pendant l'ouverture rafraîchit juste le résumé).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ajoute le MCP dédié à l'édition de templates : catalogue et classification
des templates (mcp/templates.rs infrastructure + app-tauri), use cases et
provider (application/template), enforcement de la policy des tools (mod.rs,
server.rs, tools.rs), avec la parité côté chemin OpenAI-compatible
(openai_tools.rs x2).
Lots B1 (catalogue/classification), B2 (use cases/provider) et B3
(enforcement policy) livrés en un seul commit cohérent.
QA vert (seul l'échec de bind loopback #80, connu et non-régression, écarté).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ticket #82, lot UX/Frontend : surface de gestion des permissions des tools
MCP par agent (Permissions panel), consommant l'API Tauri livrée en B4. QA
vert (858/858, tsc propre, stable sur shuffle hormis le flake #85, hors
périmètre).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds the "Tools MCP IdeA" tab to the project Permissions panel, alongside
the existing "Système" (file/command) tab. Lets the user grant/revoke MCP
tool capabilities per agent or project-wide default, grouped by domain
(Lecture projet, Lecture tickets, Délégation agents, Contexte et mémoire,
Tickets, Travail et exécution, Skills) instead of a flat 25-checkbox list,
per the UX conception in carnet #82.
- domain/ports/adapters (Tauri, HTTP, mock): wire get_mcp_tool_permissions,
update_project_mcp_tool_permissions, update_agent_mcp_tool_permissions
(already merged backend API, #82 lots B1-B4) onto PermissionGateway.
- useMcpToolPermissions: view-model owning the durable MCP tool policy
document, distinct from the file/command permissions in usePermissions.
- McpToolPermissionsPanel: target selector (Défaut projet + agents with
Hérité/Override badges) and grouped editor — inherited agents are
read-only until "Créer un override" (prefilled with the effective
allowlist), per-row Ajouté/Retiré diffing against the project default,
inline confirmation before granting a write tool at project-default
level, and unsaved-draft protection on target change.
- mcpToolGroups.ts: presentational-only domain grouping and short French
labels — the read/write classification itself always comes from the
backend-provided catalogue, never hardcoded here.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Deux reformatages sans changement de comportement (device.rs, web-server/lib.rs),
laissés de côté hors périmètre au fil de plusieurs tickets précédents.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ticket #82 : catalogue et permissions des tools MCP, backend complet en 4
lots — B1 domaine/store durable, B2 enforcement au serveur MCP stdio, B3
parité enforcement sur le chemin OpenAI-compatible, B4 API Tauri pour la
future UI de gestion. QA vert sur chaque lot.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Expose au niveau application/DTO/commandes Tauri le catalogue et les
permissions des tools MCP (application/mcp_tool_permissions.rs, dto.rs,
commands.rs) pour une future UI de gestion.
Lot B4 du ticket #82, dernier lot backend : ferme la boucle sur B1
(domaine/store) + B2 (enforcement MCP stdio) + B3 (parité
OpenAI-compatible).
QA vert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
backend/src/openai_tools.rs et app-tauri/src/openai_tools.rs appliquent
désormais les mêmes règles de permission du domaine mcp_tool_permissions
(B1) et le même enforcement que le serveur MCP natif (B2), pour fermer
l'écart de parité sur le chemin OpenAI-compatible.
Lot B3 du ticket #82 : parité posée sur B1+B2, l'API backend pour la future
UI suit en B4 sur la même branche.
QA vert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
orchestrator/mcp/server.rs applique désormais les règles de permission du
domaine mcp_tool_permissions (lot B1) à l'invocation d'un tool, y compris
le cas requester vide.
Lot B2 du ticket #82 : ferme la boucle enforcement sur le socle B1, la
parité OpenAI-compatible suit en B3 sur la même branche.
QA vert (32 tests dont le nouveau cas requester vide).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Introduit le domaine mcp_tool_permissions (règles de permission par tool
MCP) et étend le port de store correspondant. Le catalogue read/write des
tools MCP (orchestrator/mcp/tools.rs) s'appuie désormais sur ces règles, et
un store durable (mcp_tool_permission.rs) persiste les permissions au-delà
d'une session.
Lot B1 du ticket #82 : pose le socle domaine/store, l'enforcement suit en
B2 sur la même branche.
QA vert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ticket #62 : identité requester explicite pour les sessions structurées et
policy des tools OpenAI-compatible (ToolPolicyRegistry branché sur
AppOpenAiToolInvoker). QA vert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AgentSessionFactory::start propage désormais l'identité du requester aux
sessions structurées ; OpenTicketAssistant et LaunchAgent la portent
correctement de bout en bout. ToolPolicyRegistry est branché sur
AppOpenAiToolInvoker pour combler le trou de parité : TicketToolProvider
n'appliquait pas la policy des tools sur le chemin OpenAI-compatible,
contrairement au chemin structuré natif.
QA vert (échecs de bind loopback écartés comme non-régression préexistante,
tracés en #80).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ticket #79 : fix du flake PermissionsPanel > saves project defaults
(PermissionEditor draft resync clobbering in-flight edits). QA vert sur le
périmètre du ticket (2/2 stable + suite complète non-shuffled) ; le rouge
observé en shuffle vient d'un autre test préexistant sans rapport
(setCellAgent.test.tsx, tracé séparément en #85).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ticket #78 : alignement des libellés Settings desktop sur le français, QA
vert (850/850 tests, tsc propre).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
UX decision (carnet #78): the human UI of IdeA is French by default,
uniform per surface — proper nouns and technical acronyms (URL, LAN,
IP/CIDR, HTTPS, API, CLI…) stay as-is. Settings mixed English (AI
Profiles, Deployment) with French (Appareils, frozen by UX for #77).
Renames the Settings menu/nav, the Profils IA and Déploiement panels
(titles, actions, states, help text) to French, per the carnet's
exhaustive list. Updates the affected tests accordingly.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The draft-resync useEffect fired on every new `draft` object (initial load,
refresh, another save), even when its content matched `local`. If it flushed
after the user started editing, it silently discarded the edit — flaky in
tests where a passive effect can settle after a synchronous fireEvent
sequence, and a real risk in production if a fetch lands mid-edit. Now it
only resyncs when `local` still matches the last-adopted draft.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ticket #61 : refit différé des cellules terminal après split/merge, QA vert
(850/850 tests, tsc propre, test refitSignal dédié).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Le ResizeObserver de TerminalView ne déclenche pas toujours un événement
utile quand une cellule voisine apparaît/disparaît (split/merge), forçant
l'utilisateur à redimensionner la fenêtre pour rafraîchir le scaling xterm.
useLayout expose un layoutVersion bumpé à chaque commit d'arbre (chargement
initial inclus), relayé par LayoutGrid comme refitSignal à chaque
TerminalView survivant pour déclencher fit.fit() sans rouvrir le PTY.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
`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>
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>
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>
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>
#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>
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>
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>
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>
#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>