Commit Graph

353 Commits

Author SHA1 Message Date
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