---
issueRef: "#65"
version: 7
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedAt: 1784210420516
---
# Ticket #65 — Serveur headless `idea-serve` (carnet de chantier)

Statut : **QA — en attente de validation live utilisateur (non-régression desktop)**.
Branche : `feature/ticket65-idea-serve-headless` (base `develop` @ `506d589`). Local, aucune action sortante.

## Arbitrages Architect (tranchés)
- **DTO/events → propriétaire canonique = crate `backend`.** PAS de crate `presentation-dto`/`backend-api` : `backend` est déjà le composition root transport-neutre et la façade des use cases pour adapters entrants. `web-server` ET `app-tauri` le consomment ⇒ contrat canonique partagé entre deux driving adapters.
- **Frontière** : les DTO dans `backend` ne tirent NI Tauri, NI HTTP/WS/cookies, NI Axum/Hyper, NI UI. `domain`/`application` ne dépendent PAS des DTO. Vérifié QA.
- **Dédup events = dans L1, pas en dette** (sinon deux sources de vérité au moment où #65 stabilise le contrat). Dette acceptable uniquement pour le Tauri-spécifique (relay `app_handle.emit`, wiring runtime).

## Structure cible (livrée)
- `crates/backend/src/dto.rs` + `crates/backend/src/events.rs` ; `lib.rs` expose `pub mod dto; pub mod events;`
- `app-tauri/src/dto.rs` = shim `pub use backend::dto::*;`
- `app-tauri/src/events.rs` = relay Tauri uniquement, DTO importés de `backend::events`.
- `web-server` consomme `backend::{dto, events}`.

## Livré — L1/L2/L3 + run_embedded
Commits : `82e8e77` (wip de sûreté, ne fige rien) · `ddbea7b` (dédup DTO events).
`web_server::run_embedded(...)` présent : `ServerConfig`, `EmbeddedServerHandle`, `url()`, `pairing_code()`, `state()`, `stop()`, shutdown via `oneshot`. **C'est le point d'extension qui prépare #68** (le desktop consommera `web-server` comme lib, pas de fork).

## Vérif QA (exécution réelle) — VERT
- `cargo build -p web-server --bin idea-serve` + `-p app-tauri --bin app-tauri` : OK.
- `cargo test -p backend` 41 passed · `-p web-server` 55 passed · `-p app-tauri` 35 passed.
- **Invariant clé** : `ldd target/debug/idea-serve | rg -i 'webkit|javascriptcore|gtk|gdk|soup|tauri|wry'` → **aucune sortie**. Comparatif `app-tauri` tire bien libwebkit2gtk/libgtk/libsoup/libjavascriptcore.
- Contrat HTTP/WS inchangé (55 tests web-server : pair/invoke/logout/ws/static/allowlist/B8). `git diff --name-only 506d589...HEAD -- frontend` → **vide**.

## Faux mystère élucidé (à ne pas refaire paniquer)
`cargo test -p app-tauri` est passé de **45 → 35** : les tests ne sont PAS perdus, ils ont MIGRÉ. Écart réel depuis develop = **68 tests déplacés** (`src/lib.rs` : 104 → 36) : 55 `server::tests::*` → `web-server`, 10 `events::tests::*` → `backend::events`, 3 `dto::ls6_pagination_tests::*` → `backend::dto`. Vérifié par `cargo test -p app-tauri -- --list` comparé sur `506d589`.

## Faux négatif connu (arbitré NON bloquant)
`cargo test --workspace` : 10 échecs `infrastructure::session::openai_compat::*` + `factory_routes_openai_compatible_to_http_session` sur `bind: Os { code: 1, kind: PermissionDenied }` = la sandbox interdit l'écoute réseau. **Confirmé préexistant sur `506d589`** (même liste). Validation retenue par crates ciblés.

## Réserve QA restante
Aucun test dédié à `run_embedded().stop()` (un test de bind réel serait soumis à la même restriction sandbox). À couvrir dans #68, qui consommera ce handle.

## Incident de topologie (résolu — leçon)
DevBackend a d'abord travaillé sur la branche de #69 sans committer. Git a rapatrié le diff Rust (stash `-u` limité à `Cargo.lock Cargo.toml crates/`), intégrité vérifiée par empreinte SHA-256 avant/après (`c1aecea8…c499d3b` / `701bc7ee…4d6ff016`), rien perdu. **Leçon : vérifier la branche courante AVANT de commencer.**

## Prochaine étape
Validation live utilisateur : (1) le desktop ne perd rien, (2) `idea-serve` sert le client web. Si OK → Git merge dans develop (Git refuse de merger sans QA réelle sur les DEUX binaires ; point sensible = workspace `Cargo.toml` members). Débloque ensuite #68 (via `run_embedded`) et #66 (Docker).
