test(persistence): P8a — déterminisme registre + cohérence de clé end-to-end

Scelle la cohérence corrigée en P8a :
- infra (5) : resolve stable à travers 2 instances de registre (restart),
  resolve.id == for_pair == uuid agent, commutativité, distinction des paires
- application (2) : round-trip réel « handoff sauvé sous la clé resolve ↔
  rechargé au relancement de la cellule portant cette clé » + contre-épreuve
  (handoff d'une autre paire ⇒ pas de reprise)

Clarifie : une cellule neuve (conversation_id=None) ne charge pas de handoff ;
la reprise opère à la réouverture. domain/infra/application verts, zéro
régression. Code de prod non modifié.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 18:27:09 +02:00
parent e583b2b49f
commit 09cc8f0902
2 changed files with 186 additions and 1 deletions

View File

@ -194,4 +194,94 @@ mod tests {
let reg = InMemoryConversationRegistry::new();
assert!(reg.get(ConversationId::new_random()).is_none());
}
// ---------------------------------------------------------------------------
// Bloc 1 — déterminisme du registre (§19 : la clé de persistance = id de paire,
// déterministe et **stable au redémarrage**). Scelle l'invariant que Main a
// rattrapé : deux instances neuves du registre (= deux démarrages de l'IDE)
// doivent dériver **le même** id pour la même paire, et cet id doit être
// **exactement** `ConversationId::for_pair`, sinon la clé sous laquelle le
// handoff/log est rangé dériverait au redémarrage.
// ---------------------------------------------------------------------------
#[test]
fn resolve_is_stable_across_registry_restart() {
// Deux instances neuves (by_pair/by_id vides) simulent deux démarrages de
// l'IDE : la même paire User↔Agent doit produire le **même** id.
let a = agent(7);
let first = InMemoryConversationRegistry::new()
.resolve(ConversationParty::User, a)
.id;
let second = InMemoryConversationRegistry::new()
.resolve(ConversationParty::User, a)
.id;
assert_eq!(
first, second,
"id de paire stable au redémarrage (registre neuf ⇒ même id)"
);
}
#[test]
fn resolve_is_stable_across_registry_restart_agent_agent() {
// Même garantie pour une paire Agent↔Agent (dérivation XOR commutative).
let x = agent(11);
let y = agent(13);
let first = InMemoryConversationRegistry::new().resolve(x, y).id;
let second = InMemoryConversationRegistry::new().resolve(y, x).id;
assert_eq!(
first, second,
"id de paire Agent↔Agent stable au redémarrage, insensible à l'ordre"
);
}
#[test]
fn resolve_id_equals_for_pair_user_agent() {
// Alignement de clé : l'id que le registre matérialise == le repli pur
// `for_pair` == `from_uuid(agent)` (la clé que P8a/`resolve_conversation`
// dérivent pour la paire canonique User↔Agent).
let agent_id = AgentId::from_uuid(uuid::Uuid::from_u128(42));
let party = ConversationParty::agent(agent_id);
let resolved = InMemoryConversationRegistry::new()
.resolve(ConversationParty::User, party)
.id;
assert_eq!(
resolved,
ConversationId::for_pair(ConversationParty::User, party),
"resolve == for_pair (alignement de clé)"
);
assert_eq!(
resolved,
ConversationId::from_uuid(agent_id.as_uuid()),
"User↔Agent ⇒ id == uuid de l'agent (repli resolve_conversation)"
);
}
#[test]
fn resolve_id_equals_for_pair_agent_agent_commutative() {
// Commutativité et alignement sur `for_pair` pour Agent↔Agent.
let x = agent(101);
let y = agent(202);
let reg = InMemoryConversationRegistry::new();
let id_xy = reg.resolve(x, y).id;
let id_yx = reg.resolve(y, x).id;
assert_eq!(id_xy, id_yx, "resolve(a,b) == resolve(b,a)");
assert_eq!(
id_xy,
ConversationId::for_pair(x, y),
"resolve == for_pair (Agent↔Agent)"
);
assert_eq!(reg.len(), 1, "une seule conversation pour la paire {{a,b}}");
}
#[test]
fn distinct_pairs_yield_distinct_ids_across_kinds() {
// Deux paires distinctes ⇒ deux ids distincts (pas de collision de clé).
let reg = InMemoryConversationRegistry::new();
let user_a = reg.resolve(ConversationParty::User, agent(1)).id;
let user_b = reg.resolve(ConversationParty::User, agent(2)).id;
let a_b = reg.resolve(agent(1), agent(2)).id;
assert_ne!(user_a, user_b, "User↔A ≠ User↔B");
assert_ne!(user_a, a_b, "User↔A ≠ A↔B");
assert_ne!(user_b, a_b, "User↔B ≠ A↔B");
}
}