Tests LS4 — couche application de la feature « limites de session » : `SessionLimitService` (crates/application/src/agent/session_limit.rs) + réconciliation T4 dans crates/application/src/agent/structured.rs. Branche feature/agent-session-limits. DevBackend a livré (cargo build -p application OK, 0 warning ; tests compilent). Écris et exécute les tests, avec des FAKES des ports (Clock fixe, Scheduler, EventBus, AgentResumer), dans le style des tests applicatifs existants (regarde comment structured.rs et les autres use cases sont testés, réutilise les fakes existants si présents).

SessionLimitService — (a) détection→planification :
- `on_rate_limited(agent, node, conv_id, Some(reset_futur))` → le fake Scheduler reçoit exactement 1 `arm(fire_at_ms, ScheduledTask::ResumeAgent{agent_id, node_id, conversation_id})` avec fire_at_ms cohérent (= resets_at, ou now si passé via plan_resume) ; le fake EventBus reçoit `AgentRateLimited{agent_id, resets_at_ms}` PUIS `AgentResumeScheduled{agent_id, fire_at_ms}` DANS CET ORDRE ;
- `on_rate_limited(..., None)` → AUCUN arm ; events `AgentRateLimited{None}` puis `AgentRateLimitSuspected{None}` ;
- dédoublonnage (§21.10-4) : deux `on_rate_limited` successifs pour le MÊME agent → l'ancien ScheduleId est cancel-é sur le Scheduler (sans émettre d'AgentResumeCancelled pour le dédoublonnage interne).

(b) exécution de la reprise :
- `execute_resume(ScheduledTask::ResumeAgent{...})` → le fake AgentResumer voit `resume(agent_id, node_id, conversation_id, RESUME_PROMPT)` (vérifie que le prompt passé == la const RESUME_PROMPT) ; EventBus reçoit `AgentResumed{agent_id}` ; l'entrée interne est retirée (un cancel_resume ultérieur → false) ;
- AgentResumer qui retourne Err → l'erreur est propagée ET `AgentResumed` n'est PAS publié.

(c) annulation :
- `cancel_resume(agent)` après un `on_rate_limited` armé, fake Scheduler renvoyant true → retourne true, EventBus reçoit `AgentResumeCancelled{agent_id}` ;
- `cancel_resume(agent)` sans armement préalable → false, aucun event ;
- fake Scheduler renvoyant false (cas « déjà tiré ») → cancel_resume renvoie false ET n'émet PAS AgentResumeCancelled (la reprise suit son cours) — c'est le contrat anti-course important.

Réconciliation T4 (drain_with_readiness_outcome / drain_to_final) :
- flux `[RateLimited{Some(t)}]` sans Final → `Ok(TurnOutcome::RateLimited{Some(t)})` (fin gracieuse, PAS d'Err) ;
- flux `[RateLimited{None}]` sans Final → `Ok(TurnOutcome::RateLimited{None})` ;
- flux `[..., RateLimited, Final]` → `Ok(TurnOutcome::Completed(contenu))` (Final l'emporte) ;
- flux `[TextDelta]` seul (ni Final ni RateLimited) → `Err(Io)` INCHANGÉ (non-régression critique : un vrai flux tronqué reste une erreur) ;
- NON-RÉGRESSION : `send_blocking` et `drain_with_readiness` (signatures historiques) gardent leur comportement (RateLimited→Err(Io) côté send_blocking ; flux sans Final → Io). Vérifie que les tests existants (drain_with_readiness_lot1, send_blocking_d1, drain_marks_alive) restent verts.

Exécute `cargo test -p application`. Rends : rapport complet (nb tests, pass/fail), confirmation explicite que les tests applicatifs préexistants restent verts (zéro régression), et en cas d'échec un rapport CLAIR (attendu vs obtenu) pour DevBackend. Ne modifie QUE des tests ; signale tout vrai bug sans corriger la prod.


✅ LS4 — Tests couche application « limites de session » : TOUT VERT, zéro régression.

Commande : `cargo test -p application`
Résultat : **tous les binaires de test verts, 0 failed** sur l'ensemble du crate. 16 nouveaux tests LS4 ajoutés, répartis en 2 fichiers d'intégration (style des tests applicatifs existants, 100 % fakes des ports).

Confirmation explicite NON-RÉGRESSION : les suites préexistantes citées restent vertes —
- `drain_with_readiness_lot1.rs` : **7/7** ✓
- `send_blocking_d1.rs` : **9/9** ✓
- (et tout le reste du crate : project/agent_lifecycle/orchestrator/structured_launch/etc., 0 échec).

=== Nouveau fichier `tests/session_limit_service.rs` (9 tests) — fakes Clock fixe / Scheduler enregistreur-contrôlable / EventBus espion / AgentResumer espion-contrôlable ===
(a) détection→planification :
- `on_rate_limited_future_arms_and_emits_in_order` : Some(reset futur) ⇒ EXACTEMENT 1 `arm(fire_at_ms==reset, ResumeAgent{agent,node,conv})` + events `AgentRateLimited` PUIS `AgentResumeScheduled` dans cet ordre.
- `on_rate_limited_past_reset_clamps_fire_at_to_now` : reset passé ⇒ `fire_at_ms==now` (clamp anti-passé) ; l'event RateLimited garde l'heure brute passée, ResumeScheduled porte le now clampé.
- `on_rate_limited_without_reset_is_human_fallback_no_arm` : None ⇒ AUCUN arm + events `AgentRateLimited{None}` puis `AgentRateLimitSuspected{None}`.
- `on_rate_limited_twice_same_agent_dedups_cancelling_previous` : 2 signaux même agent ⇒ l'ancien ScheduleId est cancel-é avant réarmement, et AUCUN `AgentResumeCancelled` émis (dédoublonnage interne silencieux, §21.10-4).
(b) exécution :
- `execute_resume_calls_resumer_with_prompt_and_emits_resumed` : resume(agent,node,conv, prompt==RESUME_PROMPT vérifié) + `AgentResumed` publié + entrée retirée (cancel_resume ultérieur ⇒ false).
- `execute_resume_propagates_error_without_emitting_resumed` : Resumer Err ⇒ erreur propagée ET `AgentResumed` NON publié.
(c) annulation :
- `cancel_resume_after_arm_returns_true_and_emits_cancelled` : cancel renvoyant true ⇒ true + bon ScheduleId passé + `AgentResumeCancelled` publié.
- `cancel_resume_without_arm_is_false_no_event` : sans armement ⇒ false, Scheduler non sollicité, aucun event.
- `cancel_resume_when_scheduler_already_fired_is_false_no_event` : Scheduler renvoyant false (« déjà tiré ») ⇒ false ET PAS d'`AgentResumeCancelled` (contrat anti-course important).

=== Nouveau fichier `tests/session_limit_t4.rs` (7 tests) — réconciliation §21.2-T4, fakes AgentSession + InputMediator ===
- `outcome_rate_limited_some_without_final_is_graceful` : `[RateLimited{Some(t)}]` ⇒ `Ok(TurnOutcome::RateLimited{Some(t)})` (PAS d'Err) + vérifie qu'un RateLimited ne marque PAS Idle.
- `outcome_rate_limited_none_without_final_is_graceful` : `[RateLimited{None}]` ⇒ `Ok(RateLimited{None})`.
- `outcome_rate_limited_then_final_is_completed` : `[.., RateLimited, Final]` ⇒ `Completed(contenu)` (Final l'emporte) + Idle marqué.
- `outcome_truncated_stream_without_final_or_ratelimit_is_io_error` : `[TextDelta]` seul ⇒ `Err(Io)` INCHANGÉ (non-régression critique du flux tronqué).
- non-régression signatures historiques : `drain_with_readiness_rate_limited_is_io_error`, `send_blocking_rate_limited_is_io_error` (limite ⇒ Io), `drain_with_readiness_nominal_still_completes` (cas nominal Final intact).

Aucun bug détecté : le code DevBackend est conforme au cadrage §21.5 sur tous les axes (ordre des events, clamp via plan_resume, dédoublonnage silencieux, prompt constant, propagation d'erreur sans event, contrat anti-course du cancel). Je n'ai modifié AUCUNE ligne de prod (uniquement ajouté 2 fichiers de tests).

Feature LS4 validée côté tests application. Prêt pour la suite du cycle (Git : merge éventuel).


Tests LS5 — parser regex niveau 2 (infra) : `RateLimitParser` (crates/infrastructure/src/ratelimit/mod.rs) + le module d'heure partagé `timeparse` (crates/infrastructure/src/timeparse.rs). Branche feature/agent-session-limits. DevBackend a livré (cargo build -p infrastructure OK, 0 warning ; tests LS2 toujours verts). Tout est pur (now_ms injecté), testable sans I/O. Écris et exécute les tests dans le style existant.

RateLimitParser (new + detect + applies) :
- `new` sur regex INVALIDE → None (jamais de panique) ;
- pattern qui matche SANS reset_capture → `Some(SessionLimit{ resets_at_ms: None, source: Pattern, detected_at_ms == now_ms })` ;
- capture nommée `(?P<reset>...)` + time_format ABSOLU : `epoch_s` (secondes→×1000), `epoch_ms` (tel quel), `iso8601`/`rfc3339` (`...Z` → ms attendus) → resets_at_ms corrects ;
- time_format RELATIF (`relative_s`, capture « 600 », now=T) → `Some(resets_at_ms == T + 600_000)` ;
- time_format MURAL (`wall`, capture « 3pm ») : now correspondant à 10h du jour → 15h AUJOURD'HUI (même jour UTC) ; now correspondant à 16h → 15h DEMAIN (passage de minuit, +24h). Choisis des now_ms calculés proprement (epoch connu) et calcule l'attendu à la main ;
- pattern NE matche PAS → detect → None ;
- pattern matche mais capture absente/valeur pourrie/non parsable → `Some(SessionLimit{ resets_at_ms: None })` (détection utile sans heure) ;
- vérifie que `source == RateLimitSource::Pattern` dans tous les cas détectés ;
- compilation du regex faite une seule fois (à new) — au minimum vérifie que detect peut être appelé plusieurs fois sans souci.

applies(profile) :
- profil avec structured_adapter (structuré) → false (même s'il a un rate_limit_pattern) ;
- profil PTY (sans structured_adapter) AVEC rate_limit_pattern → true ;
- profil PTY SANS rate_limit_pattern → false.

timeparse (fonctions réexportées) :
- `days_from_civil` : 1970-01-01 → 0 ; une année bissextile (2024-02-29) cohérente ;
- `parse_wall_clock` : « 3pm » → (15,0,0) ; « 15:00:30 » → (15,0,30) ; format invalide → None ;
- `wall_clock_to_ms` : même jour vs lendemain selon now ;
- `parse_absolute_ms` : epoch s/ms/ISO cohérents (recoupe avec LS2 si utile).
- NON-RÉGRESSION : confirme que les tests LS2 (parse_reset_ms/parse_event dans session/mod.rs) restent verts après le refactor qui a déplacé les helpers vers timeparse.

Exécute `cargo test -p infrastructure`. Rends : rapport complet (nb tests, pass/fail), confirmation explicite que LS2/LS3 restent verts, et en cas d'échec rapport CLAIR (attendu vs obtenu) pour DevBackend.

EN PLUS (petit nettoyage que DevBackend t'a signalé) : tes tests scheduler LS3 (crates/infrastructure/src/scheduler/mod.rs, ~lignes 253/285) déclenchent 2 warnings `irrefutable if let` car ScheduledTask est mono-variante aujourd'hui. Corrige ces 2 warnings dans le code de TEST (ex. déstructuration directe au lieu de `if let`) pour garder la suite sans warning — c'est du test, tu peux le modifier. Ne touche à aucun code de prod ; signale tout vrai bug sans le corriger.


✅ LS5 — Tests parser regex niveau 2 (`RateLimitParser`) + module `timeparse` : TOUT VERT, 0 warning, zéro régression.

Commande : `cargo test -p infrastructure`
Résultat lib : **221 passed; 0 failed; 0 ignored** (195 antérieurs + **26 nouveaux LS5**). Tous les fichiers d'intégration + doctests verts. **Build de test : 0 warning** (vérifié `--no-run | grep -c warning` = 0).

Confirmation explicite NON-RÉGRESSION :
- **LS2** (`session::tests::parse_reset_ms*` / `parse_event*` / `send_emits_rate*`) : tous verts APRÈS le refactor qui a déplacé les helpers vers `timeparse` ✓
- **LS3** (`scheduler::tests::*`) : 7/7 verts ✓ (+ les 2 warnings `irrefutable if let` corrigés — voir plus bas)

=== `ratelimit/mod.rs` — 15 tests (in-file `#[cfg(test)] mod tests`) ===
new + detect :
- `new_returns_none_on_invalid_regex` : regex invalide `"rate limit ("` ⇒ None (jamais de panique).
- `detect_returns_none_when_pattern_does_not_match` : pas de match ⇒ None.
- `detect_match_without_reset_capture_has_no_time` : match sans reset_capture ⇒ `SessionLimit{resets_at_ms:None, source:Pattern, detected_at_ms==now}`.
- formats ABSOLUS : `detect_epoch_seconds_format` (×1000), `detect_epoch_millis_format` (tel quel), `detect_iso8601_format` (`2023-11-14T22:13:20Z`→1_700_000_000_000).
- format RELATIF : `detect_relative_seconds_format_uses_now` (capture « 600 », now=T ⇒ T+600_000).
- format MURAL (passage de minuit, math calculée à la main sur DAY_START=1_699_920_000_000 = 2023-11-14T00:00Z) : `detect_wall_clock_same_day_when_future` (now=10h, « 3pm » ⇒ 15h même jour) ; `detect_wall_clock_next_day_when_past` (now=16h ⇒ 15h DEMAIN, +24h).
- capture inexploitable ⇒ détection sans heure : `detect_match_with_missing_capture_group_has_no_time`, `detect_match_with_unparsable_value_has_no_time` (⇒ `resets_at_ms:None`).
- `detect_can_be_called_multiple_times` : regex compilé une seule fois, detect appelé 3× sans souci.
- `source == RateLimitSource::Pattern` asserté dans tous les cas détectés.
applies(profile) :
- `applies_false_for_structured_profile_even_with_pattern` (structuré ⇒ false, niveau 1 prend la main) ;
- `applies_true_for_pty_profile_with_pattern` (PTY + pattern ⇒ true) ;
- `applies_false_for_pty_profile_without_pattern` (PTY sans pattern ⇒ false).

=== `timeparse.rs` — 11 tests ===
- `days_from_civil_epoch_is_zero` (1970-01-01→0) ; `days_from_civil_leap_day_2024` (2024-02-29→19782, + cohérence avec le 1ᵉʳ mars).
- `parse_wall_clock_variants` (« 3pm »→(15,0,0), « 15:00:30 »→(15,0,30), « 3:30 pm »→(15,30,0), 12am→0h, 12pm→12h) ; `parse_wall_clock_rejects_invalid` (texte, 25:00, 10:75, trop de composantes ⇒ None).
- `wall_clock_to_ms_*` : même jour / lendemain / cas-limite « cible == now » (⇒ lendemain, jamais le présent).
- `parse_absolute_ms_*` : epoch s/ms, ISO8601, garbage→None (recoupe LS2).
- `parse_rfc3339_offsets_convert_to_utc` : +01:00 et −01:00 ⇒ même instant UTC.

=== Nettoyage LS3 demandé ===
Les 2 warnings `irrefutable if let` (scheduler/mod.rs ~253/285, `ScheduledTask` mono-variante) sont corrigés : remplacés par une **déstructuration directe** `let ScheduledTask::ResumeAgent { conversation_id, .. } = task;` (code de TEST uniquement). Suite désormais 0 warning.

Aucun bug détecté : le code DevBackend est conforme au cadrage §21 niveau 2 sur tous les axes (robustesse regex, stratégies de format absolu/relatif/mural, passage de minuit UTC, règle anti-double-détection `applies`, factorisation `timeparse` sans casser LS2). Je n'ai modifié AUCUN code de prod (uniquement ajouté des tests + corrigé mes propres tests LS3).

Feature LS5 validée côté tests infra. Prêt pour la suite du cycle.


Tâche QA — tests unitaires de la slice LS7-front (UI limites de session des agents), branche `feature/agent-session-limits`. DevFrontend vient d'implémenter (typecheck `tsc --noEmit` propre, 39 tests agents existants toujours verts, non commité).

Périmètre à couvrir (frontend/) :
1. `src/features/agents/useAgents.ts` — nouvel état `limitByAgent: Record<string, AgentLimitState>` (`{ limitedUntil?, resumeFireAt?, suspected? }`) peuplé depuis 5 DOMAIN_EVENT dans l'abonnement `onDomainEvent`. À tester (via le mock gateway qui émet des events) :
   - `agentRateLimited` {agentId, resetsAtMs} → entrée `{ limitedUntil: resetsAtMs, suspected: false }`.
   - `agentResumeScheduled` {agentId, fireAtMs} → arme `resumeFireAt`.
   - `agentResumeCancelled` {agentId} → retire `resumeFireAt`, reste limité.
   - `agentResumed` {agentId} → supprime l'entrée (état effacé).
   - `agentRateLimitSuspected` {agentId, resetsAtMs?} → `{ ..., suspected: true }`, y compris le cas SANS `resetsAtMs` (heure inconnue).
   - séquence réaliste : rateLimited → resumeScheduled → cancelResume (action) → vérifier retrait optimiste + appel `input.cancelResume` (mock `cancelledResumes` / `cancelResumeResult`).
   - Action `cancelResume(agentId)` exposée par le hook : retrait optimiste + verdict backend renvoyé (teste les deux verdicts via `cancelResumeResult`).
2. `src/features/agents/AgentLimitBadge.tsx` — helpers purs exportés `formatResetTime(epochMs)` (→ HH:MM) et `formatCountdown(remainingMs)` (→ « Xm Ys »/« Ys », clampé à 0 pour négatif). Teste ces helpers purs directement (cas limites : 0, négatif, < 60 s, > 60 s, minuit). Teste le rendu du badge (limité jusqu'à HH:MM vs « limité » sans heure vs « heure inconnue » pour suspected sans resetsAtMs) et que le bouton « Annuler la reprise » appelle l'action quand `resumeFireAt` est armé.
3. Adapter mock `src/adapters/mock/index.ts` — `MockInputGateway.cancelResume` (`cancelledResumes`, `cancelResumeResult`) : couvre via les tests du hook.

Suis le style des tests existants (cf. `singletonAgent.test.tsx`, `mock.test.ts`, les tests agents). Exécute la suite (`npm test` ou l'outil du repo) et RENDS-MOI via idea_reply : la commande exacte lancée, le résultat réel (nb tests passés/échoués), et le rapport d'erreurs complet si rouge. Si un test révèle un bug d'implémentation, décris-le précisément (fichier:ligne, attendu vs obtenu) — je relaierai à DevFrontend pour correction. Ne commit rien.


QA LS7-front — VERT. Aucun bug d'implémentation trouvé. Rien commité.

## Fichiers de test ajoutés (2)
- `frontend/src/features/agents/useAgentsLimits.test.tsx` (13 tests) — état `limitByAgent` du hook via le `MockSystemGateway` qui émet les events + `MockInputGateway`.
- `frontend/src/features/agents/AgentLimitBadge.test.tsx` (11 tests) — helpers purs + rendu du badge.

## Commandes exactes lancées (cwd = frontend/)
1. `npx vitest run src/features/agents/useAgentsLimits.test.tsx src/features/agents/AgentLimitBadge.test.tsx`
   → **Test Files 2 passed (2) · Tests 24 passed (24)**
2. `npx vitest run src/features/agents/` (suite agents complète, régression)
   → **Test Files 4 passed (4) · Tests 63 passed (63)** (39 existants + 24 nouveaux, 0 régression)

## Couverture livrée
useAgents (§21), tous via events réels :
- `agentRateLimited{resetsAtMs}` → `{limitedUntil, resumeFireAt:undefined, suspected:false}` ✓
- `agentResumeScheduled{fireAtMs}` → arme `resumeFireAt` par-dessus l'état limité ✓
- `agentResumeCancelled` → retire `resumeFireAt`, reste limité ✓ ; + no-op sur agent inconnu (aucune entrée créée) ✓
- `agentResumed` → entrée supprimée (`agentId in map === false`) ✓
- `agentRateLimitSuspected` AVEC et SANS `resetsAtMs` → `suspected:true`, `limitedUntil` undefined dans le cas sans heure ✓
- séquence réaliste rateLimited→scheduled→`cancelResume(action)` : retrait optimiste du countdown + agent toujours limité + `input.cancelledResumes === [agentId]` + verdict ✓
- action `cancelResume` : verdict `true` ET `false` (via `cancelResumeResult`), + appel du port même sans reprise armée ✓
- deux agents foldés indépendamment ✓

AgentLimitBadge :
- `formatCountdown` : 0→"0s", négatif→"0s" (clamp), <60s→"Xs", ceil partiel (4.2s→"5s"), ≥60s→"Xm Ys" (60000→"1m 0s", 125000→"2m 5s") ✓
- `formatResetTime` : HH:MM sans secondes (même minute +30s = même libellé), minuit stable ✓
- rendu : "limité jusqu'à HH:MM" (heure connue) / "limité" (sans heure) / note "heure inconnue — reprise à préciser" (suspected sans heure) + absence de la note quand suspected A une heure ✓
- bouton "Annuler la reprise" : appelle `onCancelResume` au clic quand `resumeFireAt` armé, countdown rendu, bouton désactivé si `busy` ✓

MockInputGateway.cancelResume (`cancelledResumes`/`cancelResumeResult`) : couvert via les tests du hook.

## Note hors-périmètre (PRÉEXISTANTE, pas causée par LS7-front)
`src/adapters/mock/mock.test.ts:16` échoue (1 test) : il assert « thirteen gateways » mais le `permission` gateway porte le compte à 14. Vérifié : la gateway `permission` existe déjà dans HEAD committé (eca2ba9) — l'assertion de comptage est simplement périmée, sans rapport avec les limites de session. Mes deux fichiers ne touchent pas ce test. Aucune action de ma part (hors scope) ; à relayer pour une mise à jour du compteur si tu veux la suite `adapters/mock` 100% verte.


Tâche QA — tests unitaires LS8-backend (filet humain niveau 3), branche `feature/agent-session-limits`. DevBackend a implémenté (build 0 erreur/0 warning, suites domain/application/app-tauri déjà vertes, non commité).

À COUVRIR — nouvelle méthode `SessionLimitService::confirm_human_resume(agent_id, node_id, conversation_id, resets_at_ms: i64)` dans `crates/application/src/agent/session_limit.rs`. Teste via les fakes existants (Clock/Scheduler/EventBus) du fichier `crates/application/tests/session_limit_service.rs` (suis ce style). Cas (cadrage Architect) :
(a) heure FUTURE ⇒ publie `AgentRateLimited{Some(resets_at_ms)}` puis `AgentResumeScheduled{fire_at_ms == resets_at_ms}` + un ScheduleId armé sur le scheduler.
(b) heure PASSÉE (resets_at_ms < now) ⇒ clamp : `fire_at_ms == now` (reprise quasi-immédiate). Vérifie l'événement `AgentResumeScheduled` avec fire_at_ms == now.
(c) DÉDOUBLONNAGE croisé : `confirm_human_resume` APRÈS un `on_rate_limited` déjà armé pour le même agent ⇒ le précédent réveil est désarmé, une seule entrée armée (et l'inverse : `on_rate_limited` après `confirm_human_resume`). Vérifie qu'il n'y a qu'un seul armement actif.
(d) ANNULABILITÉ : `cancel_resume(agent_id)` après `confirm_human_resume` ⇒ publie `AgentResumeCancelled` et renvoie true (l'armement humain s'annule par la même voie).
(e) PARITÉ auto/humain : même séquence d'événements qu'`on_rate_limited` cas Scheduled (à reset identique).

À COUVRIR — commande `set_resume_at` (app-tauri) si testable au niveau intégration comme `session_limit_wiring.rs` : au minimum le cas `node_for_agent == None` ⇒ erreur NOT_FOUND (pas d'armement orphelin). Si le câblage rend ce test trop lourd (besoin d'une cellule vivante), note-le et concentre-toi sur la couverture application (a–e) qui est l'essentiel.

Exécute (`cargo test -p application` + `-p app-tauri` si pertinent). RENDS-MOI via idea_reply : commande exacte, résultat réel (passés/échoués), rapport d'erreurs complet si rouge. Si un test révèle un bug (notamment sur le dédoublonnage croisé ou le clamp passé), décris-le précisément (attendu vs obtenu) — je relaierai à DevBackend. Ne commit rien.


QA LS8-backend (filet humain niveau 3) — VERT. Aucun bug d'implémentation trouvé (ni sur le dédoublonnage croisé, ni sur le clamp passé). Rien commité.

## Fichiers de test modifiés (2 — ajouts only, fakes existants réutilisés)
- `crates/application/tests/session_limit_service.rs` : +6 tests `confirm_human_resume` (style des fakes Clock/Scheduler/EventBus existants).
- `crates/app-tauri/tests/session_limit_wiring.rs` : +2 tests (précondition NOT_FOUND + parité runtime humaine sur le vrai bus).

## Commandes exactes lancées + résultats réels
1. `cargo test -p application --test session_limit_service`
   → **15 passed; 0 failed** (10 existants + 5 nouveaux).
2. `cargo test -p app-tauri --test session_limit_wiring`
   → **4 passed; 0 failed** (2 existants + 2 nouveaux).
3. `cargo test -p application` (régression complète) → **toutes suites vertes, 0 failed**.
4. `cargo test -p app-tauri` (régression complète) → **toutes suites vertes, 0 failed**.

## Couverture application (a–e) — l'essentiel
- **(a) heure FUTURE** `confirm_human_resume_future_arms_and_emits_in_order` : exactement 1 `arm(fire_at_ms==resets_at_ms, ResumeAgent{agent,node,conv})`, 1 ScheduleId émis, events `AgentRateLimited{Some(reset)}` PUIS `AgentResumeScheduled{fire_at_ms==reset}` dans cet ordre. ✓
- **(b) heure PASSÉE** `confirm_human_resume_past_reset_clamps_fire_at_to_now` : `fire_at_ms == now` (clamp anti-passé) ; `AgentRateLimited` garde l'heure brute passée, `AgentResumeScheduled` porte le `now` clampé. ✓
- **(c) dédoublonnage CROISÉ** (les deux sens) :
  - `confirm_human_resume_after_auto_dedups_single_active_arm` : confirm APRÈS on_rate_limited → l'ancien ScheduleId auto est cancel-é (`cancels == [issued[0]]`), pas d'`AgentResumeCancelled` (silencieux). Unicité prouvée : 1er `cancel_resume`→true, 2e→false. ✓
  - `auto_after_confirm_human_resume_dedups_single_active_arm` : on_rate_limited APRÈS confirm → symétrique, même invariant d'unicité. ✓
- **(d) ANNULABILITÉ** `cancel_resume_after_confirm_human_resume_returns_true_and_emits_cancelled` : renvoie `true` + publie `AgentResumeCancelled` (même voie que l'auto). ✓
- **(e) PARITÉ auto/humain** `confirm_human_resume_is_event_for_event_identical_to_auto_scheduled` : à reset futur identique, `bus.events()` ET `scheduler.armed()` strictement égaux entre `confirm_human_resume` et `on_rate_limited`. La source Human vs Structured n'a aucun effet observable. ✓

## Couverture app-tauri `set_resume_at`
- **NOT_FOUND** `set_resume_at_resolves_no_cell_for_an_agent_without_a_live_session` : ✓ couvert au niveau précondition. NOTE : la commande `#[tauri::command] set_resume_at` exige `State<AppState>`, non constructible hors runtime Tauri — le harnais wiring existant n'invoque jamais les commandes, seulement le service. J'ai donc testé la **précondition exacte** qui produit le NOT_FOUND : sur un `AppState::build` neuf, `structured_sessions.node_for_agent(unknown)` ET `terminal_sessions.node_for_agent(unknown)` renvoient `None` → la branche `ok_or_else(NotFound)` (commands.rs:1420-1428) est prise → aucun armement orphelin. C'est la couverture maximale réaliste sans faire tourner Tauri.
- **Parité runtime** `confirm_human_resume_arms_a_cancellable_resume_over_the_real_bus` : sur le vrai `TokioBroadcastEventBus`, `confirm_human_resume` publie `AgentRateLimited` puis `AgentResumeScheduled` et l'armement est annulable (`cancel_resume`→true), exactement comme la branche auto déjà testée. ✓

## Observation (non bloquante, pas un bug)
`confirm_human_resume` est total et défensif : le cas `ResumePlan::HumanFallback` y est inatteignable (`resets_at_ms` toujours `Some`) → traité en no-op via `if let Scheduled`. Conforme au cadrage Architect ; pas de test dédié (branche morte par construction).

Build app-tauri OK (0 erreur). Rien n'a été commité.


Tâche QA — validation LS8-front (formulaire de saisie d'heure, filet humain niveau 3) sur `feature/agent-session-limits`. DevFrontend a implémenté ET ajusté/ajouté des tests (typecheck propre, `npx vitest run src/features/agents/ src/adapters/mock/` = 109 tests verts). Ton rôle : valider la couverture, exécuter, et signaler tout trou ou bug.

Points à vérifier/exécuter :
1. Helper pur `timeInputToEpochMs(value, now)` (AgentLimitBadge.tsx) : cas couverts ? « HH:MM » valide → aujourd'hui à cette heure ; instant passé dans la journée → epoch < now (le backend clampe, pas le front) ; malformé/vide/`25:00`/`12:60` → `null`. Ajoute les cas manquants si besoin.
2. Soumission du formulaire (état suspected SANS heure) ⇒ `onSetResumeAt` / action hook `setResumeAt` appelée avec l'epoch attendu ; bouton désactivé si saisie vide ou `busy` ; saisie malformée (`null`) ne déclenche PAS d'appel.
3. Action hook `useAgents.setResumeAt(agentId, resetsAtMs)` : délègue bien au port `input.setResumeAt`, aucune mutation optimiste (vérifie que l'état ne bascule QUE sur réception de `agentResumeScheduled`, pas avant). Mock instrumenté `resumeArmings`.
4. Bascule complète : agent suspected sans heure → submit form → émettre `agentResumeScheduled` (via mock) → le badge passe à l'état nominal (countdown + bouton Annuler), le formulaire disparaît.
5. Non-régression : tout `src/features/agents/` + `src/adapters/mock/` vert. Confirme le compteur gateways corrigé (14) dans mock.test.ts.

Exécute `npx vitest run src/features/agents/ src/adapters/mock/` (+ `tsc --noEmit` si tu veux). RENDS-MOI via idea_reply : commande exacte, résultat réel (passés/échoués), trous comblés (fichiers/tests ajoutés), et tout bug trouvé (attendu vs obtenu, fichier:ligne) — je relaierai à DevFrontend. Ne commit rien.


QA rapide correctif IdeA Codex structured runtime: j'ai ajouté `PreparedContext.project_root`, relayé ce champ depuis `LaunchAgent`, puis `StructuredSessionFactory` passe `vec![ctx.project_root]` à `CodexExecSession`. `CodexExecSession` ajoute maintenant `--add-dir <project-root>` après `--sandbox workspace-write` et avant le prompt, sans `--ask-for-approval`. Tests ciblés verts: `cargo test -p infrastructure codex_new_conversation_command_carries_exact_args`, `cargo test -p infrastructure codex_resume_command_carries_exact_args`; `cargo check --workspace` vert avec warning préexistant `may_write_directly`. Peux-tu relire le risque principal et indiquer s'il manque un test critique avant rebuild AppImage ?


QA rapide terminée.

Verdict : le risque principal est bien le **pont entre le run dir isolé et le vrai project root** pour `codex exec` : si `--add-dir <project-root>` manque, est mal ordonné, ou si `--ask-for-approval` revient, Codex peut soit rester confiné au run dir, soit échouer au démarrage avec l’argument inconnu. Les deux tests ciblés protègent correctement la composition argv de `CodexExecSession` en neuf et en reprise : ordre exact, `--sandbox workspace-write`, `--add-dir /project/root` avant le prompt, et absence de `--ask-for-approval`.

Relecture du câblage :
- `PreparedContext` porte maintenant `project_root: String` dans `domain::ports`.
- `LaunchAgent` le remplit avec `input.project.root.as_str().to_owned()`.
- `StructuredSessionFactory` passe `vec![ctx.project_root.clone()]` uniquement au bras `StructuredAdapter::Codex`.
- `CodexExecSession::build_spawn_line` ajoute `--add-dir <root>` après `--sandbox workspace-write`, filtre les racines vides, puis pousse le prompt en dernier.

Test critique manquant ? Pas bloquant pour rebuild AppImage selon moi, parce que le bug runtime immédiat est verrouillé au niveau où l’argv réel est construit. Le seul test de renfort que j’ajouterais si on veut fermer complètement la régression est un test **factory-level** : démarrer un profil Codex via `StructuredSessionFactory::start(...)` avec un `PreparedContext { project_root: "/project/root" }`, fake CLI enregistreur, `send()`, puis asserter que l’argv contient bien `--add-dir /project/root`. Les tests actuels instancient `CodexExecSession` directement, donc ils ne prouvent pas explicitement que la factory relaie toujours `ctx.project_root`. Ce n’est pas un risque suffisant pour bloquer le rebuild, mais c’est le garde-fou le plus utile à ajouter ensuite.

Sortie réelle relancée :

`cargo test -p infrastructure codex_new_conversation_command_carries_exact_args` :
```text
running 1 test
test session::tests::codex_new_conversation_command_carries_exact_args ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 220 filtered out; finished in 0.00s
```

`cargo test -p infrastructure codex_resume_command_carries_exact_args` :
```text
running 1 test
test session::tests::codex_resume_command_carries_exact_args ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 220 filtered out; finished in 0.00s
```

Warning observé, conforme à ton signalement : `unused import: may_write_directly` dans `crates/application/src/orchestrator/context_guard.rs:29:25`.


Diagnostic demandé par Main : teste uniquement l’écriture effective dans le `.git` du projet IdeA, de façon minimale et réversible. Crée un fichier temporaire unique sous `/home/anthony/Documents/Projects/IdeA/.git/`, supprime-le immédiatement si la création réussit, puis rapporte le résultat exact (`WRITE_OK` ou l’erreur noyau, notamment `Read-only file system`). Ne lance pas `git add`, `git commit`, ni aucune action Git destructive. Réponds via `idea_reply` si tu reçois ce message comme tâche déléguée.


Commande exécutée (sans git add/commit ni action Git destructive) : création d’un fichier temporaire unique sous `/home/anthony/Documents/Projects/IdeA/.git/`, suppression prévue uniquement en cas de succès.

Résultat :
```text
touch: cannot touch '/home/anthony/Documents/Projects/IdeA/.git/idea-write-test-2-1781903741512743602': Read-only file system
```

Conclusion : écriture refusée par le noyau avec `Read-only file system`. Aucun fichier temporaire n’a été créé, donc aucune suppression n’était nécessaire.


Validation QA du chantier `orchestrator-designation` sur la branche courante `feature/orchestrator-designation`.

Contexte cycle:
- Git a décidé de finir ce chantier sur la branche actuelle, sans switch/stash.
- DevBackend vient de corriger `crates/application/src/orchestrator/context_guard.rs`.
- Main ne code pas; tu dois valider/qualifier.

Historique des validations:
Avant correction DevBackend:
- `cargo test -p infrastructure input --lib`: OK, 35 passed.
- `cargo test -p application --test orchestrator_service`: OK, 45 passed.
- `cd frontend && npx vitest run`: OK, 41 files / 384 tests passed.
- `cd frontend && npx tsc --noEmit`: OK.
- `cargo test --workspace`: échouait à la compilation sur `context_guard.rs` (`may_write_directly` 3e arg manquant, `AgentManifest.orchestrator` mal initialisé).

Correction DevBackend:
- `ProposeContext` charge `AgentManifest`, récupère `manifest.orchestrator_designation()`, puis appelle `may_write_directly(requester, &GuardedResource::ProjectContext, &designation)`.
- Tests locaux `context_guard.rs` adaptés au modèle `AgentManifest { version, entries, orchestrator }`.
- Le `FileGuard` reste un verrou; l'autorisation orchestrateur reste côté domaine.

Validations DevBackend après correction:
1. `cargo fmt --all && cargo test -p application --test orchestrator_service`
   - OK, `45 passed; 0 failed; 0 ignored`.
2. `cargo test -p application`
   - OK, suite application verte.
3. `cargo test --workspace`
   - Compile maintenant plus loin, puis échoue dans `app-tauri`:
   - `app-tauri` lib: `39 passed; 8 failed`.
   - Échecs: `mcp_bridge::tests::end_to_end_over_real_loopback`, `state::bind_endpoint_d1_tests::rebind_after_corpse_socket_succeeds`, et `state::mcp_e2e_loopback_tests::*`.
   - Cause observée: `PermissionDenied` / `Operation not permitted` lors du bind socket sous `/run/user/1000/idea-mcp/*.sock`, ex. `bind test listener: Os { code: 1, kind: PermissionDenied, message: "Operation not permitted" }`.

Ta mission QA:
1. Relance les tests pertinents si possible.
2. Qualifie le résidu `app-tauri`: régression code ou contrainte environnement/sandbox ?
3. Donne à Main un verdict clair: chantier validable ou correction requise.
4. Si correction requise, donne le rapport d'erreur exact pour DevBackend.
5. Si validable sous contrainte, donne la liste de commandes vertes qui font foi et la réserve exacte.

Réponds via `idea_reply` si cette tâche arrive via IdeA.


Verdict QA: chantier `orchestrator-designation` validable sous contrainte d'environnement. Je ne vois pas de régression fonctionnelle liée à la correction `context_guard.rs`.

Commandes vertes qui font foi:
- `cargo fmt --all -- --check`: OK, aucune sortie.
- `cargo test -p application --test orchestrator_service`: OK, `45 passed; 0 failed; 0 ignored`.
- `cargo test -p application`: OK, suite application complète verte; inclut `orchestrator::context_guard::tests::orchestrator_writes_global_context_directly` et `agent_proposing_global_context_files_a_proposal_not_a_write`.
- `cargo test -p infrastructure input --lib`: OK, `35 passed; 0 failed; 0 ignored; 187 filtered out`.
- `cd frontend && npx vitest run`: OK, `41 passed (41)` files, `384 passed (384)` tests.
- `cd frontend && npx tsc --noEmit`: OK, aucune sortie.

Résidu `app-tauri`:
- `cargo test -p app-tauri --lib`: ROUGE, `39 passed; 8 failed; 0 ignored`.
- `cargo test --workspace`: ROUGE sur le même bloc `app-tauri`; la compilation va désormais plus loin et ne bloque plus sur `context_guard.rs`.

Échecs exacts observés:
- `mcp_bridge::tests::end_to_end_over_real_loopback`, [crates/app-tauri/src/mcp_bridge.rs:577] : `bind test listener: Os { code: 1, kind: PermissionDenied, message: "Operation not permitted" }`.
- `state::bind_endpoint_d1_tests::rebind_after_corpse_socket_succeeds`, [crates/app-tauri/src/state.rs:4787] : `lay corpse socket: Os { code: 1, kind: PermissionDenied, message: "Operation not permitted" }`.
- `state::mcp_e2e_loopback_tests::{handshake_requester_propagates_over_real_loopback,list_agents_round_trips_over_real_loopback,ask_then_reply_round_trips_inline_over_real_loopback,ask_then_reply_round_trips_inline_over_real_loopback_codex,malformed_jsonrpc_after_handshake_errors_and_server_survives,orphan_reply_is_typed_error_over_real_loopback}`, [crates/app-tauri/src/state.rs:4208] : `M5e needs a real bound listener; bind_endpoint returned None for "/run/user/1000/idea-mcp/<id>.sock"`.

Qualification du résidu:
- Contrainte environnement/sandbox, pas régression du chantier. Une sonde Node minimale dans ce même environnement échoue à créer un listener Unix avec `EPERM` même hors `/run/user/1000`:
  - `/run/user/1000/idea-mcp/codex-bind-probe.sock` => `EPERM: listen EPERM: operation not permitted`.
  - `/tmp/codex-bind-probe.sock` => `EPERM: listen EPERM: operation not permitted`.
- Les 8 tests rouges sont précisément ceux qui exigent un vrai loopback Unix; les tests `mcp_serve_peer_tests` duplex/in-memory et les tests de wiring/domain restent verts.

Conclusion: correction DevBackend suffisante pour ce chantier. Pas de correction requise côté `orchestrator-designation`; réserve exacte: la validation globale `cargo test --workspace` reste impossible dans ce sandbox tant que les tests `app-tauri` nécessitant `listen()` sur socket Unix ne peuvent pas binder.


QA explicite pour reprise `feature/agent-skill-awareness-v2`. Contexte: hotfix livraison délégation/logs submit déjà modifié dans plusieurs fichiers + nouveau changement DevBackend dans `crates/application/src/agent/lifecycle.rs` pour awareness des skills dans `compose_convention_file`. Merci de relire/valider le périmètre et exécuter les tests pertinents réels. Minimum attendu: tests application ciblés `compose_convention_file`, `cargo test -p application`, et si tu juges nécessaire les tests déjà utilisés pour le hotfix (front vitest useWritePortal/terminal/portal, tsc, cargo test infrastructure input, cargo check app-tauri, orchestrator_service, orchestrator_wiring). Retourne commandes exactes, résultat, et tout risque restant. Ne modifie pas le code sauf test strictement nécessaire.


Verdict QA : corrections requises / non vert global.

Périmètre relu : le workspace contient bien deux ensembles de changements :
- `crates/application/src/agent/lifecycle.rs` pour `compose_convention_file` / awareness skills.
- hotfix livraison délégation/logs submit dans `crates/app-tauri/src/commands.rs`, `crates/application/src/orchestrator/service.rs`, `crates/infrastructure/src/input/mod.rs`, `frontend/src/adapters/input.ts`, `frontend/src/adapters/terminal.ts`, `frontend/src/domain/index.ts`, `frontend/src/features/terminals/useWritePortal.ts(x)`.
- Plusieurs fichiers `.ideai/**` sont aussi modifiés par l’activité runtime ; je ne les ai pas touchés.

Application / skills : vert.

Commande :
```text
cargo test -p application compose_convention_file --no-fail-fast
```
Résultat :
```text
running 15 tests
...
test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 28 filtered out; finished in 0.00s
```
Warning existant observé :
```text
warning: method `writes` is never used
    --> crates/application/tests/orchestrator_service.rs:1642:8
```

Commande :
```text
cargo test -p application
```
Résultat :
```text
running 43 tests
...
test result: ok. 43 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.10s
...
Doc-tests application
running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
```
Tous les tests d’intégration `application` inclus dans cette commande passent, dont `tests/orchestrator_service.rs`.

Hotfix input/backend : vert.

Commande :
```text
cargo test -p infrastructure input
```
Résultat :
```text
running 35 tests
...
test result: ok. 35 passed; 0 failed; 0 ignored; 0 measured; 187 filtered out; finished in 0.37s
```

Commande :
```text
cargo test -p application --test orchestrator_service
```
Résultat :
```text
running 45 tests
...
test result: ok. 45 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
```
Même warning `method writes is never used` à `crates/application/tests/orchestrator_service.rs:1642`.

Frontend : vert.

Commande :
```text
cd frontend && npx vitest run src/features/terminals/useWritePortal.test.tsx
```
Résultat :
```text
Test Files  1 passed (1)
Tests  12 passed (12)
```
Warnings Vite existants sur options `esbuild` dépréciées / `oxc` prioritaire.

Commande :
```text
cd frontend && npx vitest run src/features/terminals/TerminalView.portal.test.tsx src/features/terminals/TerminalView.test.tsx src/features/terminals/useWritePortal.test.tsx
```
Résultat :
```text
Test Files  3 passed (3)
Tests  24 passed (24)
```
Même warnings Vite.

Commande :
```text
cd frontend && npx tsc --noEmit
```
Résultat : succès, aucune sortie.

App-tauri check : vert.

Commande :
```text
cargo check -p app-tauri
```
Résultat :
```text
Checking application v0.3.0 (.../crates/application)
Checking infrastructure v0.3.0 (.../crates/infrastructure)
Checking app-tauri v0.3.0 (.../crates/app-tauri)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.29s
```

App-tauri orchestrator_wiring : rouge reproductible.

Commande :
```text
cargo test -p app-tauri --test orchestrator_wiring
```
Résultat :
```text
running 13 tests
...
test result: FAILED. 9 passed; 4 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.12s
```
Échecs réels :
```text
open_binds_the_project_loopback_endpoint
thread panicked at crates/app-tauri/tests/orchestrator_wiring.rs:253:5:
the project's loopback socket is bound on open

double_open_keeps_a_single_endpoint_no_address_in_use
thread panicked at crates/app-tauri/tests/orchestrator_wiring.rs:268:5:
assertion failed: wait_until(|| socket_exists(&project)).await

close_cleans_up_the_endpoint_socket_file
thread panicked at crates/app-tauri/tests/orchestrator_wiring.rs:289:5:
assertion failed: wait_until(|| socket_exists(&project)).await

file_watcher_and_loopback_endpoint_live_together
thread panicked at crates/app-tauri/tests/orchestrator_wiring.rs:325:5:
endpoint bound alongside the live file watcher
```

Relance en série pour exclure une course :
```text
cargo test -p app-tauri --test orchestrator_wiring -- --test-threads=1
```
Résultat : mêmes 4 échecs, `9 passed; 4 failed`.

Tests loopback/bind complémentaires : rouges aussi, ce qui pointe vers le binding socket Unix local plutôt qu’un test isolé.

Commande :
```text
cargo test -p app-tauri bind_endpoint -- --nocapture
```
Résultat :
```text
thread 'state::bind_endpoint_d1_tests::rebind_after_corpse_socket_succeeds' panicked at crates/app-tauri/src/state.rs:4787:52:
lay corpse socket: Os { code: 1, kind: PermissionDenied, message: "Operation not permitted" }
test result: FAILED. 0 passed; 1 failed
```

Commande :
```text
cargo test -p app-tauri mcp_e2e_loopback_tests -- --nocapture
```
Résultat :
```text
running 6 tests
...
M5e needs a real bound listener; bind_endpoint returned None for "/run/user/1000/idea-mcp/<project>.sock"
test result: FAILED. 0 passed; 6 failed
```

Risque restant : le MVP `agent-skill-awareness-v2` côté application est validé par tests ciblés et crate application complète. Le hotfix front/input/orchestrator_service est également vert sur les suites exécutées. En revanche, la validation `app-tauri` du loopback MCP/orchestrator wiring n’est pas verte dans l’environnement courant : création/binding de socket Unix sous `/run/user/1000/idea-mcp/*.sock` échoue (`PermissionDenied` / listener absent). Tant que cette suite reste rouge, je ne peux pas donner un vert QA global pour la reprise `feature/agent-skill-awareness-v2` avec hotfix intégré.

Je n’ai modifié aucun fichier.


Suite au rouge loopback, DevBackend a diagnostiqué une contrainte sandbox: un smoke Rust minimal `UnixListener::bind` échoue `PermissionDenied` dans `/tmp` et `/run/user/1000`, donc les tests nécessitant un vrai socket Unix ne peuvent pas passer dans cet environnement. Il a aussi exécuté avec succès les alternatives: `cargo test -p app-tauri --test orchestrator_wiring -- --skip open_binds_the_project_loopback_endpoint --skip double_open_keeps_a_single_endpoint_no_address_in_use --skip close_cleans_up_the_endpoint_socket_file --skip file_watcher_and_loopback_endpoint_live_together` => OK 9 passed, 4 filtered; `cargo test -p app-tauri --lib -- --skip state::mcp_e2e_loopback_tests --skip state::bind_endpoint_d1_tests --skip mcp_bridge::tests::end_to_end_over_real_loopback` => OK 39 passed, 8 filtered. Merci de confirmer le verdict QA final: accepter comme vert avec réserve environnementale, ou maintenir KO; liste les commandes vertes et la réserve exacte. Ne modifie pas le code.


Verdict QA final : vert accepté avec réserve environnementale.

Je ne maintiens pas le KO global, car le rouge restant est maintenant expliqué et borné : dans cet environnement sandbox, un bind de socket Unix réel échoue `PermissionDenied` même pour un smoke minimal `UnixListener::bind` dans `/tmp` et `/run/user/1000`. Les tests qui exigent un vrai socket Unix loopback ne peuvent donc pas être probants ici. Le reste du périmètre applicatif, hotfix submit/logs, frontend, wiring hors vrai socket et app-tauri lib hors tests loopback réels est vert.

Commandes vertes déjà validées QA sur ce périmètre :

```text
cargo test -p application compose_convention_file --no-fail-fast
```
Résultat : `15 passed; 0 failed; 28 filtered out`.

```text
cargo test -p application
```
Résultat : `43 passed; 0 failed` pour les unit tests application, tous les tests d’intégration application et doctests passés. Warning existant : `method writes is never used` dans `crates/application/tests/orchestrator_service.rs:1642`.

```text
cargo test -p infrastructure input
```
Résultat : `35 passed; 0 failed; 187 filtered out`.

```text
cargo test -p application --test orchestrator_service
```
Résultat : `45 passed; 0 failed` avec le même warning existant `writes`.

```text
cd frontend && npx vitest run src/features/terminals/useWritePortal.test.tsx
```
Résultat : `1 passed`, `12 tests passed`. Warnings Vite existants sur options `esbuild` dépréciées / `oxc` prioritaire.

```text
cd frontend && npx vitest run src/features/terminals/TerminalView.portal.test.tsx src/features/terminals/TerminalView.test.tsx src/features/terminals/useWritePortal.test.tsx
```
Résultat : `3 passed`, `24 tests passed`. Même warnings Vite.

```text
cd frontend && npx tsc --noEmit
```
Résultat : succès, aucune sortie.

```text
cargo check -p app-tauri
```
Résultat : succès, `Finished dev profile`.

Commandes de confirmation après diagnostic sandbox, relancées par QA :

```text
cargo test -p app-tauri --test orchestrator_wiring -- --skip open_binds_the_project_loopback_endpoint --skip double_open_keeps_a_single_endpoint_no_address_in_use --skip close_cleans_up_the_endpoint_socket_file --skip file_watcher_and_loopback_endpoint_live_together
```
Résultat réel :
```text
running 9 tests
...
test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 4 filtered out; finished in 0.01s
```

```text
cargo test -p app-tauri --lib -- --skip state::mcp_e2e_loopback_tests --skip state::bind_endpoint_d1_tests --skip mcp_bridge::tests::end_to_end_over_real_loopback
```
Résultat réel :
```text
running 39 tests
...
test result: ok. 39 passed; 0 failed; 0 ignored; 0 measured; 8 filtered out; finished in 0.01s
```

Réserve exacte : non-validé dans ce sandbox uniquement pour les tests nécessitant la création/binding d’un vrai socket Unix loopback :
- `crates/app-tauri/tests/orchestrator_wiring.rs` : `open_binds_the_project_loopback_endpoint`, `double_open_keeps_a_single_endpoint_no_address_in_use`, `close_cleans_up_the_endpoint_socket_file`, `file_watcher_and_loopback_endpoint_live_together`.
- `app-tauri --lib` : `state::mcp_e2e_loopback_tests`, `state::bind_endpoint_d1_tests`, `mcp_bridge::tests::end_to_end_over_real_loopback`.

Ces tests restent à exécuter dans un environnement non sandboxé permettant `UnixListener::bind` sur socket filesystem. Je n’ai modifié aucun fichier.


QA globale pour le chantier `feature/conversation-ux-live-state` Lot A. Périmètre implémenté: backend/Tauri read-model `get_project_work_state(projectId)` + frontend onglet/panneau Work read-only. Backend a ajouté `crates/application/src/workstate/mod.rs`, snapshots live, commande/DTO Tauri; frontend a ajouté types/domain/port/adapter/mock/hook/panel et onglet Work.

Merci de relire le périmètre: read-only, pas nouvelle persistance, pas lecture log/handoff, pas FIFO complète, ordre manifeste, live agents hors manifeste ignorés. Exécute les tests pertinents réels. Minimum:
- `cargo test -p application workstate`
- `cargo test -p application`
- `cargo check -p app-tauri`
- tests app-tauri DTO/live pertinents (`dto_agents`, `list_live_agents_r0b`) et si nécessaire app-tauri lib avec skips socket connus
- `cd frontend && npx vitest run src/features/workstate/workstate.test.tsx src/features/projects/projects.test.tsx`
- `cd frontend && npx tsc --noEmit`
Retourne verdict, commandes exactes/résultats, risques, et si les tests socket Unix restent filtrés pour raison sandbox déjà diagnostiquée. Ne modifie pas le code sauf correction de test strictement nécessaire.


Verdict QA globale Lot A `feature/conversation-ux-live-state` : vert, avec réserve environnementale non bloquante sur les tests socket Unix réels.

Périmètre relu :
- Backend application : `GetProjectWorkState` agrège en read-only depuis `AgentContextStore::load_manifest`, `LiveSessions::live_agent_snapshots()` et `InputMediator::busy_state`.
- Pas de nouvelle persistance observée dans le read-model ; pas de lecture log/handoff ; pas de modélisation FIFO complète.
- Ordre manifeste conservé par itération sur `manifest.entries`.
- Live agents hors manifeste ignorés par construction, car seuls les agents du manifeste sont émis.
- Snapshot live typé ajouté sans changer `live_agents()` existant.
- Tauri : commande `get_project_work_state(projectId)` câblée dans `commands.rs`, `state.rs`, `lib.rs`, DTO camelCase avec `live.kind` et `busy.state/sinceMs`.
- Frontend : onglet `Work`, port/gateway/adapters/mock/hook/panel ajoutés ; panneau read-only, appelle uniquement `getProjectWorkState`, affiche live/offline + busy/idle, refresh manuel et refresh sur événements existants.

Commandes exécutées et résultats :

```text
cargo fmt -p application -p app-tauri -- --check
```
Résultat : OK, aucune sortie.

```text
cargo test -p application workstate
```
Résultat :
```text
running 5 tests
test workstate_attaches_live_pty_session_to_manifest_agent ... ok
test workstate_includes_busy_state_from_input_mediator ... ok
test workstate_attaches_live_structured_session_to_manifest_agent ... ok
test workstate_ignores_live_agents_absent_from_manifest ... ok
test workstate_lists_manifest_agents_idle_without_live_sessions ... ok

test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
```
Warning existant observé :
```text
warning: method `writes` is never used
    --> crates/application/tests/orchestrator_service.rs:1642:8
```

```text
cargo test -p application
```
Résultat :
```text
running 43 tests
...
test result: ok. 43 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
...
Running tests/workstate.rs
running 5 tests
...
test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

Doc-tests application
running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
```
Même warning existant `CapturingFs::writes` unused.

```text
cargo check -p app-tauri
```
Résultat :
```text
Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.70s
```

```text
cargo test -p app-tauri --test dto_agents
```
Résultat :
```text
running 19 tests
...
test project_work_state_dto_serialises_live_and_busy_camelcase ... ok
...
test result: ok. 19 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
```

```text
cargo test -p app-tauri --test list_live_agents_r0b
```
Résultat :
```text
running 5 tests
test no_sessions_yields_empty_list ... ok
test both_kinds_live_listed_without_duplicates ... ok
test pty_live_agent_is_listed ... ok
test same_agent_in_both_registries_is_deduplicated ... ok
test structured_live_agent_is_listed ... ok

test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
```

```text
cargo test -p app-tauri --lib -- --skip state::mcp_e2e_loopback_tests --skip state::bind_endpoint_d1_tests --skip mcp_bridge::tests::end_to_end_over_real_loopback
```
Résultat :
```text
running 39 tests
...
test result: ok. 39 passed; 0 failed; 0 ignored; 0 measured; 8 filtered out; finished in 0.01s
```
Les 8 tests filtrés restent les tests nécessitant un vrai socket Unix loopback ; le sandbox courant refuse `UnixListener::bind` avec `PermissionDenied`, diagnostic déjà établi. À rejouer hors sandbox permissif socket Unix, non bloquant pour ce Lot A.

```text
cd frontend && npx vitest run src/features/workstate/workstate.test.tsx src/features/projects/projects.test.tsx
```
Résultat :
```text
Test Files  2 passed (2)
Tests  13 passed (13)
```
Warnings Vite existants : options `esbuild` dépréciées / `oxc` prioritaire.

```text
cd frontend && npx tsc --noEmit
```
Résultat : succès, aucune sortie.

Risques restants :
- Couverture volontairement limitée au read-model minimal live/busy ; pas de FIFO complète, pas d’historique conversationnel, pas de lecture log/handoff.
- Tests socket Unix réels filtrés pour contrainte sandbox connue ; ils restent à exécuter dans un environnement qui autorise le bind de sockets filesystem.
- Warning existant `CapturingFs::writes` unused dans `orchestrator_service.rs`, sans lien avec ce lot.

Aucune correction requise. Je n’ai modifié aucun fichier.


Valide le Lot B complet `workstate delegation/queue snapshot` sur la branche active `feature/workstate-delegation-queue`.

Périmètre à vérifier:
- Backend/Tauri: port read-only `AgentQueueSnapshot`, snapshots FIFO d'`InMemoryMailbox`, `GetProjectWorkState.agents[].tickets`, DTO camelCase, wiring `AppState`.
- Frontend: types `tickets`, mock normalisé, panneau Work affiche tickets FIFO inProgress/queued, refresh sur `delegationReady`.
- Les tickets human et agent sont inclus; l'UI ne doit pas les présenter tous comme délégations agent.

Commandes minimales demandées (adapte si le repo impose un cwd):
- `cargo fmt --all -- --check`
- `cargo test -p infrastructure mailbox --lib`
- `cargo test -p application --test workstate`
- `cargo test -p app-tauri --test dto_agents`
- `cargo check -p app-tauri`
- `cd frontend && npx vitest run src/features/workstate/workstate.test.tsx src/features/projects/projects.test.tsx`
- `cd frontend && npx tsc --noEmit`

Tu peux ajouter des commandes pertinentes, mais ne committe pas et ne modifie pas les fichiers applicatifs sauf tests QA indispensables. Ignore le dirty runtime `.ideai/*` s'il est seulement live-session.

Retour attendu: verdict clair, commandes exécutées avec résultats réels, anomalies éventuelles avec fichiers/lignes, et si rouge, donne la sortie d'erreur exploitable.


Verdict: VERT pour le Lot B `workstate delegation/queue snapshot` sur `feature/workstate-delegation-queue`.

Je n’ai modifié aucun fichier. Dirty ignoré côté runtime `.ideai/*`; les fichiers applicatifs/test du lot étaient déjà modifiés dans le worktree.

Commandes exécutées et résultats réels:

1. `cargo fmt --all -- --check`
   - Exit 0, aucune sortie.

2. `cargo test -p infrastructure mailbox --lib`
   - Exit 0.
   - `running 13 tests`
   - `test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 215 filtered out; finished in 0.00s`
   - Couvre notamment `snapshot_preserves_fifo_order_and_positions`, `snapshot_carries_ticket_metadata`, `snapshot_is_read_only`, updates après cancel/resolve.

3. `cargo test -p application --test workstate`
   - Exit 0.
   - `running 12 tests`
   - `test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s`
   - Couvre `workstate_lists_two_tickets_in_fifo_order`, `workstate_marks_busy_head_in_progress_and_rest_queued`, `workstate_marks_all_queued_when_agent_idle`, `workstate_maps_human_and_agent_ticket_sources`, queue absente/agent hors manifest.

4. `cargo test -p app-tauri --test dto_agents`
   - Exit 0.
   - `running 20 tests`
   - `test result: ok. 20 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s`
   - Couvre `project_work_state_dto_serialises_tickets_camelcase`: `tickets`, `inProgress`, source `agent.agentId`, source `human`, absence de snake_case.

5. `cargo check -p app-tauri`
   - Exit 0.
   - `Finished dev profile [unoptimized + debuginfo] target(s) in 0.59s`

6. `cd frontend && npx vitest run src/features/workstate/workstate.test.tsx src/features/projects/projects.test.tsx`
   - Exit 0.
   - Warnings Vite non bloquants: options `esbuild` dépréciées/ignorées au profit de `oxc`.
   - `Test Files 2 passed (2)`
   - `Tests 17 passed (17)`
   - `Duration 2.02s`

7. `cd frontend && npx tsc --noEmit`
   - Exit 0, aucune sortie.

8. Commande QA ajoutée, car `crates/domain/src/mailbox.rs` est touché par le lot: `cargo test -p domain mailbox --lib`
   - Exit 0.
   - `running 6 tests`
   - `test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 170 filtered out; finished in 0.00s`

Vérifications de périmètre:

- Backend/Tauri:
  - Port read-only présent: `domain::mailbox::AgentQueueSnapshot` dans `crates/domain/src/mailbox.rs:69`.
  - `InMemoryMailbox` implémente la snapshot read-only dans `crates/infrastructure/src/mailbox/mod.rs:166`; tests FIFO/métadonnées/read-only verts.
  - `GetProjectWorkState` consomme `Arc<dyn AgentQueueSnapshot>` dans `crates/application/src/workstate/mod.rs:130` et mappe `agents[].tickets`; tests application verts.
  - DTO `tickets` camelCase dans `crates/app-tauri/src/dto.rs:1602` et test camelCase dédié vert.
  - Wiring `AppState`: même `InMemoryMailbox` exposé comme `AgentMailbox` et `AgentQueueSnapshot` dans `crates/app-tauri/src/state.rs:994-999`, puis injecté dans `GetProjectWorkState::new` vers `state.rs:1034-1038`.

- Frontend:
  - Types `tickets` présents dans `frontend/src/domain/index.ts:153-179`.
  - Mock normalisé avec fallback `tickets: []` dans `frontend/src/adapters/mock/index.ts:1670-1684`.
  - Panneau Work trie les tickets par `position` avant affichage dans `frontend/src/features/workstate/ProjectWorkStatePanel.tsx:65`.
  - UI distingue source humaine et agent: `Human` pour `source.kind === "human"`, sinon `label (agent-id-prefix)` dans `ProjectWorkStatePanel.tsx:21-27`; test `renders an in-progress human ticket` et test FIFO agent verts.
  - Refresh sur `delegationReady` couvert par `frontend/src/features/workstate/workstate.test.tsx:223` et vert.

Anomalies: aucune anomalie bloquante détectée. Les seuls messages observés sont les warnings Vite sur la config `esbuild` dépréciée, sans échec de test ni impact Lot B constaté.


Valide le Lot C complet `workstate conversation summaries` sur la branche active `feature/workstate-conversation-summaries`.

Périmètre:
- Backend/Tauri: `ProjectWorkState.conversations` top-level, previews best-effort depuis `HandoffStore` puis `ConversationLog::last(3)`, dédup des conversation ids issus des tickets, DTO camelCase.
- Frontend: types TS, mock normalise `conversations: []`, `ProjectWorkStatePanel` joint `tickets[].conversationId` vers `conversations[]` et affiche badge + preview compacte.
- Aucune nouvelle persistance, aucun log brut exposé, aucune action UX.

Commandes minimales demandées:
- `cargo fmt --all -- --check`
- `cargo test -p application --test workstate`
- `cargo test -p app-tauri --test dto_agents`
- `cargo check -p app-tauri`
- `cd frontend && npx vitest run src/features/workstate/workstate.test.tsx src/features/projects/projects.test.tsx`
- `cd frontend && npx tsc --noEmit`

Ajoute une commande infra/domain seulement si tu juges nécessaire après inspection. Ne committe pas. Ignore dirty runtime `.ideai/*` s'il est seulement live-session.

Retour attendu: verdict clair, commandes exécutées avec résultats réels, anomalies éventuelles avec fichiers/lignes, et si rouge, sortie d'erreur exploitable.


Verdict: VERT pour le Lot C `workstate conversation summaries` sur `feature/workstate-conversation-summaries`.

Je n’ai modifié aucun fichier et je n’ai pas committé. Dirty runtime `.ideai/*` ignoré; le dirty restant correspond aux fichiers du lot déjà présents dans le worktree.

Commandes exécutées et résultats réels:

1. `cargo fmt --all -- --check`
   - Exit 0, aucune sortie.

2. `cargo test -p application --test workstate`
   - Exit 0.
   - Warning non bloquant:
     - `warning: variant Absent is never constructed`
     - `crates/application/tests/workstate.rs:173:5`
     - `application` test `workstate` generated 1 warning.
   - Résultat:
     - `running 21 tests`
     - `test result: ok. 21 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s`
   - Tests pertinents verts: `workstate_conversation_ready_from_handoff`, `workstate_conversation_missing_with_bounded_recent_turns`, `workstate_conversation_partial_when_handoff_errors_but_log_ok`, `workstate_conversation_unavailable_when_handoff_and_log_fail`, `workstate_dedups_conversation_ids_from_tickets`, `workstate_preview_failure_preserves_agents_live_busy_tickets`, `workstate_conversation_previews_truncated_and_normalised`.

3. `cargo test -p app-tauri --test dto_agents`
   - Exit 0.
   - `running 21 tests`
   - `test result: ok. 21 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s`
   - Test pertinent vert: `project_work_state_dto_serialises_conversations_camelcase`.

4. `cargo check -p app-tauri`
   - Exit 0.
   - `Finished dev profile [unoptimized + debuginfo] target(s) in 0.58s`

5. `cd frontend && npx vitest run src/features/workstate/workstate.test.tsx src/features/projects/projects.test.tsx`
   - Exit 0.
   - Warnings Vite non bloquants:
     - `esbuild option was specified by vite:react-babel plugin. This option is deprecated, please use oxc instead.`
     - `optimizeDeps.esbuildOptions option was specified by vite:react-babel plugin. This option is deprecated, please use optimizeDeps.rolldownOptions instead.`
     - `Both esbuild and oxc options were set. oxc options will be used and esbuild options will be ignored.`
   - Résultat:
     - `Test Files 2 passed (2)`
     - `Tests 19 passed (19)`
     - `Duration 2.10s`

6. `cd frontend && npx tsc --noEmit`
   - Exit 0, aucune sortie.

Je n’ai pas ajouté de commande infra/domain: après inspection, le lot touche le read-model application, le wiring Tauri, les DTO et le frontend; les tests ciblés couvrent directement ces surfaces.

Vérifications de périmètre:

- Backend/Tauri:
  - `ProjectWorkState.conversations` top-level présent dans `crates/application/src/workstate/mod.rs:50-57`.
  - `ConversationWorkSummary` best-effort, read-only, sans projection durable, défini dans `crates/application/src/workstate/mod.rs:60-81`.
  - Provider `ConversationLogProvider` per-project-root dans `crates/application/src/workstate/mod.rs:112-121`.
  - Wiring optionnel `with_conversation_sources` dans `crates/application/src/workstate/mod.rs:242-253`.
  - Dédup des conversation ids issus des tickets en ordre first-seen/FIFO dans `crates/application/src/workstate/mod.rs:345-357`.
  - Algorithme handoff primaire puis fallback `ConversationLog::last(_, 3)` dans `crates/application/src/workstate/mod.rs:360-454`; `RECENT_TURNS_MAX = 3` à `workstate/mod.rs:35-36`.
  - Câblage Tauri des providers dans `crates/app-tauri/src/state.rs:121-128` et injection via `.with_conversation_sources(...)` à `state.rs:1063-1066`.
  - DTO camelCase `ConversationWorkSummaryDto` / `ProjectWorkStateDto.conversations` dans `crates/app-tauri/src/dto.rs:1610-1738`, validé par le test DTO.

- Frontend:
  - Types TS `ConversationPreviewStatus`, `ConversationTurnWorkPreview`, `ConversationWorkSummary`, et `ProjectWorkState.conversations` présents dans `frontend/src/domain/index.ts:173-212`.
  - Mock normalise `conversations: []` dans `frontend/src/adapters/mock/index.ts:1666-1686`.
  - `ProjectWorkStatePanel` construit une map `conversations` par `conversationId` dans `frontend/src/features/workstate/ProjectWorkStatePanel.tsx:196-204`.
  - Les tickets sont joints via `conversations.get(ticket.conversationId)` dans `ProjectWorkStatePanel.tsx:183-188`.
  - Affichage compact badge + preview dans `ProjectWorkStatePanel.tsx:35-75`; pas de nouvelle action UX constatée.
  - Tests frontend verts pour objectif/résumé joint par conversation id, partial/unavailable compact, tickets conservés.

Anomalies:

- Non bloquant: warning Rust dans le test `crates/application/tests/workstate.rs:173:5`, variante `HandoffOutcome::Absent` jamais construite. Aucun échec associé.
- Non bloquant: warnings Vite sur options `esbuild` dépréciées/ignorées. Aucun impact Lot C constaté.

Aucune anomalie bloquante détectée.


Revalide rapidement le Lot C après nettoyage du warning `HandoffOutcome::Absent` dans `crates/application/tests/workstate.rs`.

Merci de relancer au minimum:
- `cargo fmt --all -- --check`
- `cargo test -p application --test workstate`
- `cargo test -p app-tauri --test dto_agents`
- `cd frontend && npx vitest run src/features/workstate/workstate.test.tsx src/features/projects/projects.test.tsx`
- `cd frontend && npx tsc --noEmit`

Vérifie surtout qu'il n'y a plus le warning Rust signalé. Ne committe pas. Retourne verdict et résultats réels.


Verdict: VERT après revalidation rapide du Lot C sur `feature/workstate-conversation-summaries`.

Aucun fichier modifié, aucun commit. Le warning Rust précédemment signalé sur `HandoffOutcome::Absent` a bien disparu: `cargo test -p application --test workstate` ne produit plus de warning.

Commandes exécutées et résultats réels:

1. `cargo fmt --all -- --check`
   - Exit 0, aucune sortie.

2. `cargo test -p application --test workstate`
   - Exit 0.
   - Aucune sortie `warning:`.
   - `running 21 tests`
   - `test result: ok. 21 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s`

3. `cargo test -p app-tauri --test dto_agents`
   - Exit 0.
   - `running 21 tests`
   - `test result: ok. 21 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s`

4. `cd frontend && npx vitest run src/features/workstate/workstate.test.tsx src/features/projects/projects.test.tsx`
   - Exit 0.
   - Warnings Vite non bloquants toujours présents sur les options `esbuild` dépréciées/ignorées au profit de `oxc`.
   - `Test Files 2 passed (2)`
   - `Tests 19 passed (19)`
   - `Duration 2.08s`

5. `cd frontend && npx tsc --noEmit`
   - Exit 0, aucune sortie.

Anomalies: aucune anomalie bloquante. Le warning Rust demandé est résolu.