Capitalise la mémoire projet accumulée pendant les chantiers B7/B8 (tâches de fond first-class), le système de tickets V1 et le ticket #1 : design, cadrages d'archi, checkpoints d'avancement et verdicts QA/frontend. Mise à jour de l'index MEMORY.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6.9 KiB
name, description, metadata
| name | description | metadata | ||
|---|---|---|---|---|
| background-tasks-first-class-design | memory note background-tasks-first-class-design |
|
Tâches de fond de 1re classe — CADRAGE FIGÉ (Architect, 2026-07-02)
Base : feature/background-tasks-first-class (@ fccc1e2, empilée sur v2 62915ee+fccc1e2,
au-dessus de develop a9653bc, ligne CLI/PTY « toujours headless »).
Objectif (2 défauts à corriger)
- Complétion post-tour perdue : une tâche de fond (ex. build
run_in_background) finit après la fin du tour de l'agent → IdeA ne ré-invoque pas le propriétaire avec le résultat. - Pas de mailbox : message concurrent pendant working/waiting rejeté « still busy » au lieu d'être mis en file et drainé au tour suivant.
Modèle
BackgroundTask { task_id, owner_agent_id, project_id, kind, state, started/updated_at_ms,
deadline_ms?, correlation, result, wake_policy }.
- kind :
Command | HeadlessRendezvous | SessionResume | Maintenance - state :
Queued | Running | Waiting | Completed | Failed | Cancelled | Expired - result :
None | Success(payload) | Failure(error) | Cancelled(reason) - wake_policy :
WakeOwner | RecordOnlyRègle centrale : la complétion n'est JAMAIS seulement un retour de future local ; elle est persistée/observée comme événement de tâche, puis transformée en message mailbox pour le propriétaire.
Réutilisé vs nouveau
Réutilisé : InputMediator/AgentMailbox (FIFO par agent), TicketId, AgentBusyState,
run_ask_with_watchdog+plafond+liveness, live-state.json (projection maigre),
ReplyEvent::Final/Announcement, session-limit scheduler (réveil différé annulable).
Nouveau : BackgroundTask persistant, store/registry, completion sink durable,
AgentWakePort, mailbox entrante bornée par agent (couvre user/agents/complétions système),
reconcile au boot.
Ports
BackgroundTaskStore: create/get/save/list_open_for_agent/list_undelivered_completions/ mark_completion_delivered. B1 figé (async_trait,BackgroundTaskPortError).BackgroundTaskRunner: spawn(spec)->handle / cancel / subscribe_completions()->stream. B1 figé.AgentInbox(façade au-dessus d'InputMediator) : enqueue_message / dequeue_next / snapshot. Lot B4.AgentWakePort: wake_agent(project, agent, reason) — ne connaît PAS Tauri ; adapter lance/rattache session structured/headless. Lot B5.
Contrats/DTO
InboxItem { id, agent_id, source: Human|Agent{id}|BackgroundTask{task_id}|System,
kind: UserMessage|AgentDelegation|BackgroundCompletion|ResumeNotice, body, created_at_ms,
correlation_id?, priority (FIFO défaut, pas de priorité cachée V1) }.
BackgroundCompletionDto camelCase { taskId, ownerAgentId, projectId, kind, status,
exitCode, stdoutTail, stderrTail, finishedAtMs }.
Events DomainEvent : BackgroundTaskStarted/Progress(borné)/Completed/Failed/Cancelled,
AgentInboxQueued/Drained, AgentWakeScheduled/Started/Failed. (Events = observabilité/UI ;
store+mailbox = autorité.)
Flux nominal run_in_background
1 agent lance commande → 2 crée BackgroundTask{owner,Running} → 3 adapter démarre → 4 tour peut finir → 5 commande finit plus tard → 6 completion sink reçoit exit/stdout/stderr → 7 écrit Completed/Failed dans store → 8 enqueue InboxItem::BackgroundCompletion → 9 si owner Idle: AgentWakePort.wake_agent démarre nouveau tour headless avec résultat → 10 si Busy/Waiting: FIFO, drainé au prochain tour libre. Idempotence : 1 seule completion livrée par task_id ; crash entre store et wake réparé par reconcile.
Mailbox bornée
1 par agent, FIFO stricte, capacité configurable (IDEA_AGENT_INBOX_CAPACITY, défaut 100).
Enqueue pendant Working/Waiting → queued (plus jamais busy). Overflow : messages
humains/agents → InboxFull typée ; complétions de tâches → persistées delivery_pending,
JAMAIS perdues. « Busy » = tour en cours, pas entrée refusée.
Lots backend
- B1 domaine : types, ports Store/Runner, events, invariants purs. ✅ FAIT (12 tests). (AgentInbox/AgentWakePort reportés à B4/B5.)
- B2 store+registry : adapter FS/sqlite-like simple, écriture atomique, reconcile boot (Running sans handle vivant → Unknown/Failed ou delivery_pending).
- B3 completion sink : runner publie complétions sur canal interne ; sink persiste AVANT tout wake ; tests idempotence double completion.
- B4 mailbox unifiée : étendre
InputMediator/AgentMailbox(pas de 2e FIFO concurrente) ; enqueue_message, snapshots workstate ; remplacer refus « still busy » parqueued. - B5 wake owner : adapter
AgentWakePortau-dessus deStructuredSessions/AgentSession::send; wake seulement si idle ; sinon launch/rattach headless ; résultat injecté comme message système explicite. - B6 rendezvous-as-task :
idea_ask_agentreste synchrone pour l'appelant mais son exécution interne = BackgroundTask{HeadlessRendezvous} ; timeouts/backstops → résultats de tâche, plus des états invisibles. - B7 reconcile boot : lire store, ré-enqueue complétions non livrées, recalculer live-state.
Lots frontend
- F1 workstate : queue depth par agent, « Queued » au lieu de « busy rejected ».
- F2 panel tâches de fond : liste par agent running/completed/failed ; cancel/open output/retry.
- F3 agent cell : badge « messages en attente » + « tâche de fond terminée » ; pas de transcript brut auto-injecté.
- F4 notifications : toast sobre à la fin d'une tâche longue ; clic ouvre owner/détail.
Invariants
completion persistée avant wake ; livrée au plus une fois ; aucun message vers agent connu
rejeté pour Busy ; mailbox ne dépasse jamais capacité ; overflow ne perd jamais une completion
système ; 1 item traité à la fois ; live-state = projection jamais autorité ; Final reste le
seul terminal normal headless ; un redémarrage n'oublie pas les complétions persistées non
drainées.
Critères QA (backend)
commande background finissant après le tour → owner réveillé avec exit code + résumé ; idem
avec IdeA redémarré entre fin et wake → completion retrouvée ; message user à agent busy →
queued ; deux agents vers même agent busy → FIFO ; mailbox pleine → InboxFull sur message
normal, completion système en pending ; double event même task_id → 1 livraison ; annulation →
Cancelled, pas de wake succès ; rendezvous silencieux → backstop = tâche Failed/NoReply,
libère la queue ; session-limit resume continue et ne contourne pas la mailbox.
Liens : checkpoint-b2-bootstrap-applied-await-codex-reset-1430, rendezvous-no-reply-backstop-design, session-limit-handling-design, headless-interagent-conversation-objective, inter-agent-live-context-shared-per-agent.