fix(orchestrator,plugins): contact cross-project corrélé + découvrabilité assignation plugin par agent — #284 (backend: registre des demandes actives clé projet/agent appelant + correlationId, refus corrélation vide/dupliquée, cancel routé après correspondance exacte sans redirection vers une autre cible, appelant projeté Waiting→Done dans le live-state de son propre projet avec correlationId exposé par idea_workstate_read, gardes RAII réconcilient l'abandon du futur, tools ask/cancel WriteAction deny-by-default dont le refus indique où activer la permission; frontend: action Attribuer aux agents depuis Settings projet > Plugins ouvrant le panneau Agents flottant/ancré/détaché, section Capabilities de plugins — {agent} explicitant attribution skills et tools MCP des plugins activés; QA verte: orchestrator_service 79/79, mcp_server 40/40, domain live_state 15/15, correlated_update_is_preserved_in_the_lean_projection 1/1, frontend ProjectPluginSettings 3 tests, npm run build frontend, cargo fmt --check, git diff --check; AppImage IdeA_0.3.0_amd64.AppImage rebuildée workflow build-appimage, validation live cross-project restant après remplacement/relance manuels; réserves: registre corrélation en mémoire non persisté, surface web unsupported, catalogue dynamique tools MCP = réserve #282)
This commit is contained in:
@ -106,6 +106,9 @@ pub struct LiveEntry {
|
||||
pub progress: Option<String>,
|
||||
/// The ticket of the most recent delegation this agent issued, if any.
|
||||
pub last_delegation: Option<TicketId>,
|
||||
/// External correlation carried by an active cross-project request, if any.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub correlation_id: Option<String>,
|
||||
/// Wall-clock update time in epoch milliseconds (drives `prune` ordering).
|
||||
pub updated_at_ms: u64,
|
||||
}
|
||||
@ -138,9 +141,35 @@ impl LiveEntry {
|
||||
status,
|
||||
progress,
|
||||
last_delegation,
|
||||
correlation_id: None,
|
||||
updated_at_ms,
|
||||
})
|
||||
}
|
||||
|
||||
/// Attaches a validated external correlation id to this projection.
|
||||
///
|
||||
/// Unlike human-readable preview fields, correlation ids are never truncated:
|
||||
/// changing the value would make cancellation routing ambiguous.
|
||||
pub fn with_correlation_id(
|
||||
mut self,
|
||||
correlation_id: Option<String>,
|
||||
) -> Result<Self, DomainError> {
|
||||
if let Some(value) = correlation_id {
|
||||
if value.trim().is_empty() {
|
||||
return Err(DomainError::Invariant(
|
||||
"live-state correlation id must not be empty".to_owned(),
|
||||
));
|
||||
}
|
||||
if value.len() > FIELD_MAX_BYTES {
|
||||
return Err(DomainError::Invariant(format!(
|
||||
"live-state correlation id exceeds the max size of {FIELD_MAX_BYTES} bytes ({} bytes)",
|
||||
value.len()
|
||||
)));
|
||||
}
|
||||
self.correlation_id = Some(value);
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Enforces the field bounds: reject above the hard byte threshold, otherwise
|
||||
@ -224,6 +253,7 @@ impl LiveState {
|
||||
status: WorkStatus::Idle,
|
||||
progress: Some(STALE_AT_RESTART_MARKER.to_owned()),
|
||||
last_delegation: None,
|
||||
correlation_id: e.correlation_id.clone(),
|
||||
updated_at_ms: now_ms,
|
||||
})
|
||||
.collect()
|
||||
|
||||
Reference in New Issue
Block a user