chantier: durcissement reprise headless inter-projets + persistance profils Opencode

- Implémentation reprise de session Opencode headless (conversational_recovery)
- Persistance des profils IA Opencode dans .ideai/memory avec JSON Schema
- Gestion des permissions MCP pour agents externes
- Tests QA verts : structured_launch_d3, conversation_log, tickets_missing_carnet, agents, ProfilesSettings
This commit is contained in:
2026-07-28 12:23:34 +02:00
parent 78f7c8fe2d
commit 045e0984cc
15 changed files with 782 additions and 49 deletions

View File

@ -1294,6 +1294,64 @@ async fn provider_is_isolated_per_conversation() {
);
}
#[tokio::test]
async fn provider_is_isolated_per_project_root_with_same_conversation_and_provider() {
let project_a = TempDir::new();
let project_b = TempDir::new();
let store_a = FsProviderSessionStore::new(&project_a.project_path());
let store_b = FsProviderSessionStore::new(&project_b.project_path());
let c = conv_id(77);
store_a.set(c, "opencode", "engine-a").await.unwrap();
store_b.set(c, "opencode", "engine-b").await.unwrap();
assert_eq!(
store_a.get(c, "opencode").await.unwrap(),
Some("engine-a".to_owned())
);
assert_eq!(
store_b.get(c, "opencode").await.unwrap(),
Some("engine-b".to_owned())
);
assert!(
!std::fs::read_to_string(project_a.providers_path(c))
.unwrap()
.contains("engine-b"),
"project A providers.json must not contain project B's engine id"
);
assert!(
!std::fs::read_to_string(project_b.providers_path(c))
.unwrap()
.contains("engine-a"),
"project B providers.json must not contain project A's engine id"
);
}
#[tokio::test]
async fn provider_unresolved_project_root_fails_closed() {
let root = ProjectPath::new(format!(
"{}/missing-{}",
std::env::temp_dir().to_string_lossy(),
Uuid::new_v4()
))
.unwrap();
let store = FsProviderSessionStore::new(&root);
let c = conv_id(78);
assert_eq!(
store.get(c, "opencode").await.unwrap(),
None,
"unresolved root cannot produce a resumable id"
);
assert!(
matches!(
store.set(c, "opencode", "engine").await,
Err(StoreError::Io(_))
),
"unresolved root must not write outside a canonical project root"
);
}
// ===========================================================================
// LS6 — rotation (ConversationArchive) + pagination humaine, I/O réelle.
// Réutilise le scaffolding L2 (TempDir absolu, constructeurs déterministes).