chore(wip): checkpoint P8/C avant chantier Codex inter-agents

Sauvegarde de l'arbre de travail en cours (persistance P8, conversations
C-series, write-portal frontend, médiation d'entrée) avant d'attaquer le
support de la délégation inter-agents pour les profils Codex.

Le round-trip inter-agent question/réponse est couvert sans tokens par
les tests loopback existants (state::mcp_e2e_loopback_tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-13 21:42:53 +02:00
parent 4509f0db9d
commit fdcf16c387
76 changed files with 3783 additions and 1404 deletions

View File

@ -31,9 +31,7 @@ use crate::ports::StoreError;
/// Newtype autour d'[`uuid::Uuid`], calqué sur [`crate::conversation::ConversationId`]
/// / [`crate::mailbox::TicketId`]. Sa monotonie (un id frappé à l'`append`) sert de
/// **curseur** à la relecture incrémentale ([`ConversationLog::read`] avec `since`).
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize,
)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(transparent)]
pub struct TurnId(pub uuid::Uuid);
@ -199,11 +197,7 @@ pub struct Handoff {
impl Handoff {
/// Construit un handoff à partir de ses composants.
#[must_use]
pub fn new(
summary_md: impl Into<String>,
up_to: TurnId,
objective: Option<String>,
) -> Self {
pub fn new(summary_md: impl Into<String>, up_to: TurnId, objective: Option<String>) -> Self {
Self {
summary_md: summary_md.into(),
up_to,
@ -235,11 +229,7 @@ pub trait HandoffStore: Send + Sync {
///
/// # Errors
/// [`StoreError`] en cas d'échec d'écriture ou de sérialisation.
async fn save(
&self,
conversation: ConversationId,
handoff: Handoff,
) -> Result<(), StoreError>;
async fn save(&self, conversation: ConversationId, handoff: Handoff) -> Result<(), StoreError>;
}
/// Le résumeur incrémental de handoff (port driving, lot P4).
@ -435,7 +425,10 @@ mod tests {
#[test]
fn turn_role_serializes_camel_case() {
assert_eq!(serde_json::to_string(&TurnRole::Prompt).unwrap(), "\"prompt\"");
assert_eq!(
serde_json::to_string(&TurnRole::Prompt).unwrap(),
"\"prompt\""
);
assert_eq!(
serde_json::to_string(&TurnRole::Response).unwrap(),
"\"response\""
@ -581,7 +574,11 @@ mod tests {
.unwrap();
}
let last2 = log.last(c, 2).await.unwrap();
assert_eq!(texts(&last2), vec!["c", "d"], "the 2 last, in insertion order");
assert_eq!(
texts(&last2),
vec!["c", "d"],
"the 2 last, in insertion order"
);
}
#[tokio::test]
@ -599,7 +596,10 @@ mod tests {
.await
.unwrap();
assert_eq!(texts(&log.read(c1, None).await.unwrap()), vec!["c1-a", "c1-b"]);
assert_eq!(
texts(&log.read(c1, None).await.unwrap()),
vec!["c1-a", "c1-b"]
);
assert_eq!(texts(&log.read(c2, None).await.unwrap()), vec!["c2-a"]);
// A cursor from one thread never leaks tours from another.
assert_eq!(texts(&log.last(c2, 10).await.unwrap()), vec!["c2-a"]);