feat(orchestrator): backstop no-reply du rendez-vous inter-agents
Remédiation du wedge persistant après échec live du fix Finding A (77e62e5).
Détecte la fin de tour d'un agent sollicité qui n'a pas appelé idea_reply et
débloque l'agent demandeur au lieu de le laisser en attente indéfinie.
Ajoute le suivi de tour côté inspector Claude (claude_turn_watcher) et la
résolution des chemins de session (claude_paths), câblés dans le rendez-vous
idea_ask_agent ⇄ idea_reply.
Build workspace vert, suite complète verte, zéro warning.
Backstop NON prouvé levé en live : merge develop interdit tant que la levée
du wedge n'est pas validée en conditions réelles.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -1305,6 +1305,46 @@ pub trait SessionInspector: Send + Sync {
|
||||
) -> Result<ConversationDetails, InspectError>;
|
||||
}
|
||||
|
||||
/// Callback fired **once per detected turn end** by a [`TurnWatcher`].
|
||||
///
|
||||
/// Invoked from the watcher's own polling task (never while holding a lock), so the
|
||||
/// composition root can safely route it to
|
||||
/// [`crate::input::InputMediator::turn_ended`] for the given agent.
|
||||
pub type OnTurnEnd = Arc<dyn Fn(AgentId) + Send + Sync>;
|
||||
|
||||
/// Opaque handle to a live [`TurnWatcher::watch`]. **Dropping it stops the watch**
|
||||
/// (the adapter's polling task must observe the drop and cease firing, idempotently).
|
||||
pub trait TurnWatchHandle: Send + Sync {}
|
||||
|
||||
/// Observes an agent's **end-of-turn** signal from its on-disk transcript, replacing
|
||||
/// the dead PTY prompt-ready sniff as the turn-end authority (rendez-vous no-reply
|
||||
/// backstop). Model-specific: an adapter watches the CLI's transcript shape (e.g. the
|
||||
/// Claude `turn_duration` record) and fires [`OnTurnEnd`] each time a turn completes.
|
||||
///
|
||||
/// Pure port (no async, no I/O in the trait): the adapter owns its polling task. The
|
||||
/// composition root arms one watch per **live agent session** (keyed on `cwd`, the
|
||||
/// agent's isolated run dir) and drops the handle on close/exit.
|
||||
pub trait TurnWatcher: Send + Sync {
|
||||
/// Whether this watcher knows how to read turn ends for the given profile
|
||||
/// (mirrors [`SessionInspector::supports`] — e.g. recognising `CLAUDE.md`).
|
||||
fn supports(&self, profile: &AgentProfile) -> bool;
|
||||
|
||||
/// Arms a watch over `agent`'s transcript under `cwd` (its run dir). The adapter
|
||||
/// captures a **baseline** of turn ends already present at arm time and fires
|
||||
/// `on_turn_end(agent)` only on increments **above** that baseline (so a
|
||||
/// pre-existing transcript — relaunch / background wake — never triggers a phantom
|
||||
/// turn end). `conversation_id` is an optional diagnostic label only — it is **not**
|
||||
/// the file key (the transcript stem is the engine session id, unknown at cold
|
||||
/// start). Returns a [`TurnWatchHandle`]; dropping it stops the watch.
|
||||
fn watch(
|
||||
&self,
|
||||
agent: AgentId,
|
||||
conversation_id: Option<String>,
|
||||
cwd: ProjectPath,
|
||||
on_turn_end: OnTurnEnd,
|
||||
) -> Box<dyn TurnWatchHandle>;
|
||||
}
|
||||
|
||||
/// Publish/subscribe domain events. Synchronous, in-process.
|
||||
pub trait EventBus: Send + Sync {
|
||||
/// Publishes an event.
|
||||
|
||||
Reference in New Issue
Block a user