Files
IdeA/.ideai/memory/b8-command-runner-pty-framing.md
Blomios ebd992e41a docs(memory): notes projet tâches de fond, tickets V1 et checkpoints ticket #1
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>
2026-07-04 00:21:43 +02:00

3.1 KiB

name: b8-command-runner-pty-framing description: Cadrage hexagonal figé du lot B8 : runner concret sur le port BackgroundTaskRunner, fermeture de la boucle sink en composition root, contrat de complétion, commandes cancel/retry. metadata: type: reference

B8 — Runner de commandes couplé PTY (FIGÉ Architecture, 2026-07-03)

Base feature/background-tasks-first-class. Fait suite à background-tasks-first-class-design et b7-wiring-anchor-state-rs.

Trou constaté

BackgroundCompletionSink::new/start_from_runner n'existent QUE dans les tests. En prod state.rs, background_ready_tx n'est alimenté que par le reconcile boot ; aucun impl BackgroundTaskRunner concret. LocalProcessSpawner.run = one-shot Output, non couplé.

Décisions

  • Le spawner ne crée pas la task. Concepts owner/project/wake_policy = application. On implémente le port FIGÉ BackgroundTaskRunner (domain/ports.rs:1349) via nouvel adapter infra CommandBackgroundRunner composant Arc<dyn PtyPort> (résolu via RemoteHost → Liskov SSH/WSL), PAS un PortablePtyAdapter en dur.
  • Application = use case SpawnBackgroundCommand : alloue TaskId, store.create(Queued)→Running, runner.spawn(BackgroundTaskSpec). Le spec (ports.rs:207) porte déjà task_id/project_id/owner_agent_id/kind/wake_policy/command:Option/deadline.
  • Le runner n'écrit NI store NI inbox : il émet 1 BackgroundTaskCompletion sur subscribe_completions(). Le sink (single-writer) fait store.save PUIS ready_tx.send (persist-avant-signal).
  • Composition root ferme la boucle : construire runner + BackgroundCompletionSink::new(store_port, background_ready_tx) + sink.start_from_runner(runner) sur tauri::async_runtime::spawn (pas de tokio ambiant). Ancre state.rs ~1540-1660.
  • Frontière : seuls SpawnSpec/BackgroundTaskSpec/BackgroundTaskCompletion (domaine) franchissent ; portable-pty reste en infra. Rendu xterm = tee du même spawn vers PtyBridge, orthogonal au tracking.

Contrat complétion

BackgroundTaskResult Success/Failure { finished_at_ms, exit_code:Some, summary, stdout_tail, stderr_tail bornés (ring UTF-8-safe, cap IDEA_BG_TAIL_BYTES ~8-16KiB) }. Cancel→runner kill→Cancelled{reason}, pas de wake succès. Invariants tenus par le sink (dédup task_id, IgnoredAlreadyTerminal, mark_completion_delivered) + pont enqueue_message (jamais busy) + wake si idle. Final reste terminal-de-tour, la complétion est un InboxItem::BackgroundCompletion.

Commandes Tauri (dépend B8)

cancel_background_task({taskId}); retry_background_task({taskId})→BackgroundTaskDto (NOUVEAU task_id, jamais réutilisé); list_background_tasks({projectId,agentId?}). DTO camelCase {taskId,ownerAgentId,projectId,kind,state,exitCode?,summary?,stdoutTail?,stderrTail?,createdAtMs,updatedAtMs}.

Sous-tâches DevBackend (ordre)

1 util tail borné (infra). 2 CommandBackgroundRunner (infra/background_task.rs + lib.rs). 3 use cases SpawnBackgroundCommand/Cancel/Retry (application). 4 fermer boucle sink en composition root (app-tauri/state.rs). 5 handlers+DTO (commands.rs, events.rs). 6 tee PTY live (pty.rs). 7 tests (réutiliser tests/background_completion_sink.rs).