Spec issu d'un brainstorming structuré (7 questions clarifiantes, décisions tranchées) inspiré par l'exploration comparative de 5 frameworks computer-use (Simular Agent-S, browser-use, OpenAI CUA sample, Coasty open-cu, Showlab OOTB). 3 quick wins ciblés : - QW1 multi-écrans : capture/grounding par monitor_index avec fallbacks - QW2 LoopDetector composite : screen_static (CLIP) + action_repeat + retry - QW4 safety_checks hybrides : déclaratif workflow + LLM contextuel (medgemma:4b, timeout 5s, fallback safe, kill-switch env) Contraintes inviolables : 100% vision, 100% local Ollama, backward compat. Plan livraison : QW1+QW2 avant démo GHT, QW4 enchaîné dès validation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
26 KiB
Spec — QW Suite Mai 2026
| Champ | Valeur |
|---|---|
| Date | 2026-05-05 |
| Auteur | Dom + Claude (brainstorming structuré) |
| Branche | feature/qw-suite-mai (depuis feature/feedback-bus) |
| Backup | backup/pre-qw-suite-mai-2026-05-05 à pousser sur Gitea avant 1er commit |
| Statut | Design approuvé — spec à valider par Dom avant writing-plans |
| Cibles démo | GHT Sud 95 (1ère sem mai 2026, date à confirmer) |
| Contraintes inviolables | 100% vision · 100% local (Ollama) · backward compatible |
1. Contexte & motivation
Suite à l'exploration comparative de 5 frameworks computer-use (Simular Agent-S, browser-use, OpenAI CUA sample, Coasty open-cu, Showlab OOTB), trois quick wins ont été identifiés comme améliorations à fort ratio valeur/risque pour RPA Vision V3, alignés avec la philosophie du projet (vision pure, souveraineté, supervision médicale) :
- QW1 — Multi-écrans propre (inspiré OOTB) : capture et grounding sur l'écran cible plutôt que sur le composite tous écrans. Gain de perf grounding + correction des coordonnées.
- QW2 — LoopDetector composite (inspiré browser-use) : détecter quand Léa exécute des actions techniquement valides mais que l'écran ne progresse pas, et escalader vers l'humain plutôt que de tourner en rond muettement.
- QW4 — Safety checks hybrides (inspiré OpenAI CUA + browser-use Pydantic registry) : enrichir l'action
pause_for_humanavec une liste de vérifications à acquitter, mêlant déclaratif (workflow) et contextuel (LLM local).
Effet cumulé attendu : Léa devient observable, robuste et auditable sans rien céder sur le 100% local.
2. Décisions de design (récap)
| Sujet | Décision |
|---|---|
| Activation | Default-ON pour tous les workflows (Dom recréera ce qui en a besoin) |
| QW1 — Stratégie ciblage écran | monitor_index enregistré à la capture → fallback focus actif → fallback composite (backward) |
| QW1 — Niveau de stack | Client Agent V1 (capture) + serveur (routeur) + core/execution/input_handler.py (capture locale) |
| QW2 — Signal de boucle | Composite OR : screen_static (CLIP) + action_repeat + retry_threshold |
| QW2 — Sortie | replay_state["status"] = "paused_need_help" avec pause_reason structuré |
| QW4 — Source des checks | Hybride : déclaratif workflow + LLM contextuel sur safety_level: "medical_critical" |
| QW4 — Robustesse LLM | medgemma:4b + timeout 5s + format=json Ollama + JSON Schema strict + fallback safe (zéro check additionnel) + kill-switch env var |
| QW4 — UX VWB | Bulle existante préservée + <ChecklistPanel> au-dessus de Continuer (bouton désactivé tant que required non cochés) |
| Ordre de livraison | QW1 → QW2 → QW4 (du moins invasif au plus visible) |
| Plan timing | Option A : QW1+QW2 avant démo ; QW4 enchaîné dès validation des deux premiers |
| Kill-switches | Env vars sur QW2 et QW4, surchargeables par systemctl edit |
| Backward compatibility | 100% — aucun champ obligatoire ajouté au DSL ; workflows existants se comportent comme avant |
3. Architecture globale
┌─────────────────────────┐ ┌─────────────────────────────────┐
│ Agent V1 (Windows) │ │ Serveur Streaming (5005) │
│ │ │ │
│ ┌──────────────────┐ │ │ ┌───────────────────────────┐ │
│ │ ScreenCapture │ │ │ │ LoopDetector [QW2] │ │
│ │ + monitor_index │───┼────────▶│ │ • screen_static (CLIP) │ │
│ │ [QW1] │ │ HTTP │ │ • action_repeat │ │
│ └──────────────────┘ │ │ │ • retry_threshold │ │
│ │ │ │ → paused_need_help │ │
│ ┌──────────────────┐ │ │ └───────────────────────────┘ │
│ │ FeedbackBus lea:*│◀──┼─────────┤ │
│ │ chat_window │ │ │ ┌───────────────────────────┐ │
│ └──────────────────┘ │ │ │ SafetyChecksProvider │ │
└─────────────────────────┘ │ │ [QW4] │ │
│ │ • declarative (workflow) │ │
│ │ • LLM contextual │ │
│ │ ‒ medgemma:4b 5s/JSON │ │
│ │ ‒ fallback safe │ │
│ │ • kill-switch env var │ │
│ └───────────────────────────┘ │
│ │
│ ┌───────────────────────────┐ │
│ │ MonitorRouter [QW1] │ │
│ │ • cible monitor_index │ │
│ │ • fallback focus actif │ │
│ └───────────────────────────┘ │
└─────────────────────────────────┘
│
▼
┌─────────────────────────────────┐
│ VWB Frontend (3002) │
│ │
│ PauseDialog (étendu) [QW4-UX] │
│ • bulle existante préservée │
│ • + ChecklistPanel │
│ (cases à cocher acquittables)│
│ • + pause_reason si loop │
│ Continuer désactivé tant que │
│ required-checks non cochés │
└─────────────────────────────────┘
Principes invariants
- Aucun nouveau service, aucune nouvelle DB. Tout dans la stack existante (Agent V1 + serveur 5005 + VWB 3002).
- 3 modules serveur isolés (
monitor_router.py,loop_detector.py,safety_checks_provider.py) — couplage faible, testables individuellement, désactivables par env var. - Backward compatible : workflows sans nouveaux champs se comportent comme avant.
- Kill-switches env vars sur QW2 et QW4, override possible via
systemctl editpendant la démo. - 100% vision : QW1 pure capture + grounding ; QW2 réutilise le
_clip_embedderdéjà chargé ; QW4 LLM = Ollama local strict. - Bus
lea:*étendu de 4 events d'observabilité :lea:loop_detected,lea:safety_checks_generated,lea:safety_checks_llm_failed,lea:monitor_routed.
Surface de modification (ordre A)
| QW | Fichiers nouveaux | Fichiers modifiés |
|---|---|---|
| QW1 | agent_v0/server_v1/monitor_router.py |
agent_v0/agent_v1/capture/screen_capture.py, core/execution/input_handler.py, agent_v0/server_v1/api_stream.py (~10 lignes) |
| QW2 | agent_v0/server_v1/loop_detector.py |
agent_v0/server_v1/replay_engine.py (~30 lignes), agent_v0/server_v1/api_stream.py (~20 lignes) |
| QW4 | agent_v0/server_v1/safety_checks_provider.py, visual_workflow_builder/frontend_v4/src/components/PauseDialog.tsx |
agent_v0/server_v1/replay_engine.py, agent_v0/server_v1/api_stream.py (/replay/resume), visual_workflow_builder/frontend_v4/src/types.ts, visual_workflow_builder/frontend_v4/src/components/PropertiesPanel.tsx |
4. QW1 — Multi-écrans
4.1 Composants
Client Agent V1 — agent_v0/agent_v1/capture/screen_capture.py (existant à modifier)
- Enrichit chaque heartbeat / event avec :
monitor_index: intmonitors_geometry: [{idx, x, y, w, h, primary}]
- Détection via
screeninfo(port direct depuis Showlab OOTB) - Capture de l'écran actif uniquement (poids réseau identique à aujourd'hui)
- Si
screeninfoindisponible côté Windows : envoiemonitors_geometry: [], comportement composite préservé
Serveur — nouveau agent_v0/server_v1/monitor_router.py (~80 lignes)
- API :
resolve_target_monitor(action: dict, session_state: dict) → MonitorTarget MonitorTarget = {idx, offset_x, offset_y, w, h, source: "action" | "focus" | "composite_fallback"}- Stratégie :
- Lit
action.get("monitor_index")si présent → cible cet écran - Sinon
session_state.get("last_focused_monitor")→ cible focus actif - Sinon
monitors[0]composite (comportement actuel — backward)
- Lit
Input local Linux — core/execution/input_handler.py modifs ciblées
- Signature changée :
_capture_screen(monitor_idx=None) → (image, w, h, offset_x, offset_y) - Quand
monitor_idxfourni : capture uniquement ce monitor - Toutes les fonctions
_grounding_*(_grounding_ocr,_grounding_ui_tars,_grounding_vlm) propagent l'offset pour traduire les coords retournées en coords absolues écran
4.2 Data flow replay
Action [monitor_index=1] reçue par serveur
→ MonitorRouter.resolve()
→ target_monitor = {idx:1, offset:(1920,0), w:1920, h:1080, source:"action"}
→ grounding capture monitor 1 uniquement (image 1920×1080, pas 3840×1080)
→ UI-TARS / OCR / VLM cherche cible → coords locales (640, 540)
→ coords absolues = (640+1920, 540+0) = (2560, 540)
→ pyautogui.click(2560, 540)
→ bus.emit("lea:monitor_routed", {idx:1, source:"action"})
4.3 Error handling
| Cas | Comportement |
|---|---|
monitor_index absent (vieille session) |
Fallback focus actif, log info lea:monitor_routed source=focus |
| Monitor enregistré n'existe plus (2nd écran débranché) | Fallback focus actif, event lea:monitor_unavailable warning |
mss.monitors[i] hors limites |
Fallback monitors[0] composite, event lea:monitor_invalid_index error |
screeninfo non installé côté Agent V1 |
monitors_geometry: [], fallback composite (comportement actuel) — pas de blocage |
4.4 Tests QW1
tests/unit/test_monitor_router.py: 4 cas (cible OK, fallback focus, fallback composite, monitor débranché)tests/integration/test_grounding_offset.py: capture 1 monitor + clic résolu avec offset (mock pyautogui)- Smoke : 1 workflow Easily rejoué, vérification visuelle que le clic atterrit au bon endroit
4.5 Compat workflows existants
Aucune action n'a monitor_index aujourd'hui → 100% des workflows existants partent en fallback focus actif → comportement quasi-identique au composite actuel mais sur un seul écran (gain de perf grounding même sans recréation de workflow).
5. QW2 — LoopDetector composite
5.1 Composants
Nouveau agent_v0/server_v1/loop_detector.py (~150 lignes)
- Classe
LoopDetectoravec 3 sous-détecteurs - API :
evaluate(replay_state, screenshot_history, action_history) → LoopVerdict LoopVerdict = {detected: bool, reason: str, signal: str, evidence: dict}
Hook dans agent_v0/server_v1/api_stream.py
- Après chaque
report_action_result, appelloop_detector.evaluate(...)siRPA_LOOP_DETECTOR_ENABLED=1(défaut) - Si
verdict.detected:replay_state["status"] = "paused_need_help"replay_state["pause_reason"] = verdict.reasonreplay_state["pause_message"] = f"Léa semble bloquée — {verdict.signal}"- bus.emit
lea:loop_detectedavec{signal, evidence, replay_id}
Étendu dans replay_engine.py :
_create_replay_state()ajoute :"_screenshot_history": [](anneau de 5 derniers embeddings CLIP)"_action_history": [](anneau des 5 dernières actions)
_pre_check_screen_state()continue indépendamment (signal différent : check pré-action vs détection post-action de stagnation)
5.2 Signaux composites
| Signal | Détecteur | Seuil par défaut | Source |
|---|---|---|---|
screen_static |
A | 4 captures consécutives avec CLIP similarity > 0.99 | _clip_embedder déjà chargé serveur |
action_repeat |
B | 3 actions consécutives identiques (type + coords) | _action_history |
retry_threshold |
C | 3 retries sur même action_id |
replay_state["retried_actions"] (déjà existant) |
Un seul signal positif suffit à déclencher l'escalade.
5.3 Data flow
Action exécutée → result reçu via /replay/result
↓
LoopDetector.evaluate(state, screenshots, actions) si RPA_LOOP_DETECTOR_ENABLED=1
├─ A.check_screen_static() → embed(latest), compare aux N-1 derniers
├─ B.check_action_repeat() → compare action_history[-3:]
└─ C.check_retry_threshold() → state["retried_actions"] >= 3
↓
Si verdict.detected:
state["status"] = "paused_need_help"
state["pause_reason"] = verdict.reason
state["pause_message"] = f"Léa semble bloquée — {verdict.signal} ({evidence})"
bus.emit("lea:loop_detected", {signal, evidence, replay_id})
5.4 Error handling
| Cas | Comportement |
|---|---|
| CLIP embedder unavailable | Signal A désactivé (warning log 1×), B+C continuent. Pas de blocage. |
_screenshot_history < N |
Signal A skip silencieusement (pas assez d'historique) |
embed_image() lève une exception |
Catch + log warning, replay continue (verdict = detected=False) |
RPA_LOOP_DETECTOR_ENABLED=0 |
Module entier bypassé, comportement antérieur |
| Faux positif détecté en pleine démo | RPA_LOOP_DETECTOR_ENABLED=0 via systemctl edit rpa-streaming + restart → reprise immédiate |
5.5 Configuration env vars
RPA_LOOP_DETECTOR_ENABLED=1(défaut)RPA_LOOP_SCREEN_STATIC_THRESHOLD=0.99RPA_LOOP_SCREEN_STATIC_N=4RPA_LOOP_ACTION_REPEAT_N=3RPA_LOOP_RETRY_THRESHOLD=3
5.6 Tests QW2
tests/unit/test_loop_detector.py: 8 cas (chaque signal isolé, chaque combinaison, kill-switch, embedder absent)tests/integration/test_loop_detector_replay.py: 3 cas — replay simulé qui boucle → vérifier transitionrunning → paused_need_helpavec bonne raison- Pas de smoke démo (impossible à reproduire fiable, on s'appuie sur les tests intégration)
5.7 Compat VWB
Aucune côté frontend pour QW2 : la pause paused_need_help existe déjà. Le pause_reason enrichi sera affiché par le composant PauseDialog étendu en QW4. Avant la livraison de QW4, la raison s'affichera en texte dans le pause_message (donc utile dès le commit QW2).
6. QW4 — Safety checks hybrides
6.1 Contrat de l'action étendue (rétro-compatible)
{
"type": "pause_for_human",
"parameters": {
"message": "Validation T2A avant codage",
"safety_level": "medical_critical",
"safety_checks": [
{"id": "check_ipp", "label": "Vérifier IPP patient", "required": true},
{"id": "check_cim10", "label": "Confirmer code CIM-10", "required": true}
]
}
}
safety_level et safety_checks sont optionnels. Action sans ces champs → comportement actuel (bulle simple, aucun appel LLM).
6.2 Composants serveur
Nouveau agent_v0/server_v1/safety_checks_provider.py (~180 lignes)
- API :
build_pause_payload(action, replay_state, last_screenshot) → PausePayload - Concatène : checks déclaratifs (workflow) + checks contextuels (LLM si
safety_level == "medical_critical") - Chaque check porte sa source :
source: "declarative" | "llm_contextual"et sonevidence(vide pour déclaratif, justification courte pour LLM) - Format check final :
{ "id": "check_xxx", "label": "...", "required": true, "source": "declarative" | "llm_contextual", "evidence": null | "..." }
LLM contextual call — sous-fonction _call_llm_for_contextual_checks()
- Modèle :
medgemma:4b(envRPA_SAFETY_CHECKS_LLM_MODEL) - Timeout dur : 5s (env
RPA_SAFETY_CHECKS_LLM_TIMEOUT_S) format=jsonnatif Ollama + JSON Schema strict :{"additional_checks": [{"label": "string", "evidence": "string"}]}- Max 3 checks ajoutés (env
RPA_SAFETY_CHECKS_LLM_MAX_CHECKS) - Prompt : screenshot heartbeat actuel + workflow message + liste des checks déclaratifs (évite doublons)
- Tout échec (timeout, exception, JSON invalide post-schema) →
additional_checks = [], eventlea:safety_checks_llm_failed, replay continue
Hook dans replay_engine.py — branche action_type == "pause_for_human"
- Avant de basculer en
paused_need_help, appelsafety_checks_provider.build_pause_payload(...) - Stocke
replay_state["safety_checks"] = payload.checks - Stocke
replay_state["pause_payload"] = payload(pour debug/audit)
Modif api_stream.py — endpoint /replay/resume
- Reçoit
{acknowledged_check_ids: [...]}dans le body POST - Vérifie : tous les checks
required=truedoivent être dansacknowledged_check_ids- Sinon :
400 {error: "required_checks_missing", missing: [...]}
- Sinon :
- Stocke
replay_state["checks_acknowledged"] = acknowledged_check_ids(audit trail) - Reprise normale du replay
6.3 Composants frontend VWB
Nouveau visual_workflow_builder/frontend_v4/src/components/PauseDialog.tsx (~200 lignes)
- Props :
pauseMessage,pauseReason,safetyChecks,onResume(ackIds),onCancel - Si
safetyChecks.length === 0: rend la bulle existante (legacy, comportement actuel) - Sinon : bulle +
<ChecklistPanel>avec checkboxes - Bouton Continuer disabled tant que
checks.filter(c => c.required && !checked).length > 0 - POST
/replay/resumeavec body{acknowledged_check_ids: [...]} - Visuel source :
- Badge
[Léa]poursource: "llm_contextual"(avec tooltipevidence) - Badge
[obligatoire]pourrequired: true
- Badge
Étendu types.ts
PauseAction['parameters']: ajoutsafety_level?,safety_checks?Execution: ajoutpause_reason?,safety_checks?
Étendu PropertiesPanel.tsx:1356 — éditeur de l'action pause_for_human
- Section "Niveau de sécurité" : dropdown
standard | medical_critical - Section "Checks à valider" : liste éditable (id + label + required)
6.4 Data flow complet
Action pause_for_human (medical_critical, 2 checks déclaratifs) atteinte
↓
SafetyChecksProvider.build_pause_payload()
├─ checks = [...declarative] (2 entrées)
├─ if safety_level == "medical_critical" and RPA_SAFETY_CHECKS_LLM_ENABLED=1:
│ llm_checks = _call_llm_for_contextual_checks() (max 3, timeout 5s)
│ checks += llm_checks
└─ return PausePayload(checks, pause_reason, message)
↓
replay_state["status"] = "paused_need_help"
replay_state["safety_checks"] = checks
bus.emit("lea:safety_checks_generated", {count, sources})
↓
Frontend VWB poll /replay/state → reçoit pause_payload
↓
<PauseDialog> rend ChecklistPanel
↓
Médecin coche les 4 checks → clique Continuer
↓
POST /replay/resume {acknowledged_check_ids: [4 ids]}
↓
Serveur valide (tous required acquittés) → reprise du replay
replay_state["checks_acknowledged"] = [...] (audit trail conservé)
6.5 Error handling
| Cas | Comportement |
|---|---|
safety_level absent |
Pas d'appel LLM ; checks déclaratifs uniquement (peut être []) → bulle simple si vide, checklist sinon |
| Ollama timeout 5s | Event lea:safety_checks_llm_failed, additional_checks=[], fallback safe (déclaratifs seuls) |
Ollama JSON malformé (post format=json — théoriquement impossible) |
Idem timeout, fallback safe |
| LLM produit un check absurde | Accepté tel quel, le superviseur ignore (pas de filtrage en V1) |
Frontend reçoit safety_checks=[] |
Bulle simple, comportement legacy |
RPA_SAFETY_CHECKS_LLM_ENABLED=0 |
Couche LLM bypassée, déclaratifs gardés |
/replay/resume sans acknowledged_check_ids sur required |
400 required_checks_missing |
Frontend POST /replay/resume rejeté |
Toast d'erreur côté UI, état pause conservé, possibilité de cocher manquants et réessayer |
6.6 Configuration env vars
RPA_SAFETY_CHECKS_LLM_ENABLED=1(défaut)RPA_SAFETY_CHECKS_LLM_MODEL=medgemma:4bRPA_SAFETY_CHECKS_LLM_TIMEOUT_S=5RPA_SAFETY_CHECKS_LLM_MAX_CHECKS=3
6.7 Tests QW4
tests/unit/test_safety_checks_provider.py: 7 cas (déclaratif seul, hybride réussi, LLM timeout, LLM JSON invalide, kill-switch, max_checks respecté, déclaratif vide)tests/integration/test_replay_resume_acknowledgments.py: 3 cas (resume OK, missing required → 400, audit trail enregistré danschecks_acknowledged)- Frontend :
tests/components/PauseDialog.test.tsxsi suite Vitest existe (à confirmer pendant l'implémentation), sinon test manuel avec checklist écrite - Smoke : 1 workflow Easily avec
pause_for_human medical_criticalenrichi → vérification full chain
6.8 Compat workflows existants
100% backward — pause_for_human actuels n'ont ni safety_level ni safety_checks → comportement strictement identique. Aucune recréation forcée. Dom enrichira uniquement les workflows qu'il veut promouvoir au niveau medical_critical.
7. Tests, sécurité de la branche, livraison
7.1 Filet de sécurité avant TOUT commit sur feature/qw-suite-mai
- Branche backup poussée Gitea :
backup/pre-qw-suite-mai-2026-05-05 - Capture baseline E2E :
pytest tests/test_pipeline_e2e.py \ tests/test_phase0_integration.py \ tests/integration/test_stream_processor.py \ -q 2>&1 | tee .qw-baseline.log - Smoke démo : 1 dérouler complet d'un workflow Easily Assure, archivage screenshot/vidéo de référence
- État VWB validé : démarrage Vite local, ouverture d'un workflow, lancement d'un replay simple, screenshot "tout va bien"
7.2 Discipline TDD légère par QW
- Test unitaire écrit AVANT le code de production (1 test rouge → 1 implémentation → vert)
- Pas de TDD complet sur le frontend (Vitest + React = trop d'outillage à valider en parallèle), test manuel cadré avec checklist écrite
- Re-run de la suite baseline après chaque commit QW, comparaison au log archivé
- Toute régression bloque le passage au QW suivant tant qu'elle n'est pas comprise et résolue
7.3 Compat VWB — checklist explicite avant commit QW4
- Workflow ancien (sans
safety_checks) → bulle simple s'affiche normalement - Workflow nouveau avec
safety_checksdéclaratifs uniquement → checklist visible, pas d'appel Ollama (vérification logs) - Workflow
medical_critical→ checklist + checks LLM apparaissent (vérification logs Ollama call dans les 5s) - Continuer désactivé tant que required non cochés
- POST
/replay/resumeavec mauvais payload → toast d'erreur côté UI, pas de crash - PropertiesPanel : édition de
safety_checksne casse pas l'édition d'autres params depause_for_human - DB
workflows.db: ouverture après commit, aucune migration cassante (schéma JSON est libre)
7.4 Plan de commits
1. test(qw1): tests monitor_router + grounding_offset (rouges)
2. feat(qw1): multi-écrans piloté par monitor_index (verts)
3. test(qw2): tests loop_detector composite (rouges)
4. feat(qw2): LoopDetector composite avec kill-switch env
5. test(qw4): tests safety_checks_provider + replay_resume (rouges)
6. feat(qw4): safety_checks hybride déclaratif + LLM contextuel
7. feat(vwb): PauseDialog + ChecklistPanel + extension PropertiesPanel
8. docs(qw): docs/QW_SUITE_MAI.md + mise à jour MEMORY.md
Chaque commit signé Co-Authored-By Claude. Branche poussée régulièrement sur Gitea pour backup distant.
7.5 Stratégie en cas de régression critique pendant la démo
Kill-switches env vars surchargeables sans redéploiement code :
systemctl edit rpa-streaming
# Ajouter sous [Service] :
Environment=RPA_LOOP_DETECTOR_ENABLED=0
Environment=RPA_SAFETY_CHECKS_LLM_ENABLED=0
systemctl restart rpa-streaming
Si problème grave au-delà des kill-switches : rollback à backup/pre-qw-suite-mai-2026-05-05.
git checkout backup/pre-qw-suite-mai-2026-05-05
./svc.sh restart
7.6 Plan de livraison (Option A validée)
Avant démo GHT (cette semaine) — Sprint priorité 1
- QW1 : tests + code + smoke (~1j)
- QW2 : tests + code + tests intégration (~2j)
- Capture baseline + replay smoke entre chaque
- Si QW1+QW2 validés et probants → on enchaîne sur QW4 dès que possible (Dom accepte le weekend si "effet waouh" auprès de spécialistes RPA)
Après démo / dès validation QW1+QW2 — Sprint priorité 2
- QW4 serveur (provider + LLM + endpoint resume) (~3j)
- QW4 frontend (PauseDialog + PropertiesPanel) (~2j)
- Doc + mise à jour MEMORY.md
Total estimé : ~8.5j-h ingénieur senior, étalable selon le retour démo.
8. Ce qui n'est PAS dans ce spec (out of scope)
- F1 (DSL d'actions Pydantic-first) : refactor de fond, sera son propre spec après la démo.
- F2 (Mixture-of-Grounding routeur adaptatif) : nécessite F1, son propre spec.
- F3 (Best-of-N + Reflection) : nécessite F1, son propre spec.
- QW3 (
output_model_schemaPydantic pourextract_text) : opportuniste, sera intégré quand on toucheraextract_textpour autre chose. - Toute introduction de Pydantic-AI / instructor / Playwright / accessibility-tree : interdit (contraintes inviolables).
- Refonte du composant pause en
<PauseDialog>à 3 modes (option C de Q6) : reportée après démo si retour utilisateurs justifie l'investissement.
9. Open questions
Aucune. Toutes les décisions de design ont été tranchées via les 7 questions clarifiantes du brainstorming du 5 mai 2026.