feat(skill-awareness): T1→T5 — manifeste de skills + outil MCP idea_skill_read

Surface les skills assignés à un agent « à la MCP » : description sur l'entité
Skill (+ effective_description fallback), section « # Skills disponibles » haute
altitude dans le convention-file (mode MCP), outil read-only idea_skill_read
(résolution projet→global), use case ReadSkill (port SkillStore existant), et
câblage au composition root. Dump legacy du corps complet conservé en mode
sans-MCP (zéro régression). Rétro-compat index.json (serde default).

Tests : domain+application+infrastructure 1212 passed / 0 failed (23 ajoutés).
Reste : T6 (champ description front) + T7 (e2e après rebuild AppImage).

NB topologie : commit réalisé par l'orchestrateur car l'agent Git était
injoignable (bug de livraison cold-start) ; à faire relire/rebaser par Git.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 15:08:33 +02:00
parent 7db444a31d
commit 11dc8d7ba3
13 changed files with 758 additions and 27 deletions

View File

@ -248,6 +248,17 @@ pub enum OrchestratorCommand {
/// The reading party (handshake identity).
requester: ConversationParty,
},
/// Read a reusable skill's Markdown body **by name** (`idea_skill_read`,
/// feature « skills à la MCP »). Resolution is project-scope-first then global;
/// the body is returned inline. Read-only — no [`crate::fileguard::FileGuard`]
/// lease (skills are not mutated through this path).
ReadSkill {
/// Skill display name to resolve (case-insensitive).
name: String,
/// The party that issued the read (handshake identity). Carried for
/// symmetry/auditing with the other read tools; skill reads need no lease.
requester: ConversationParty,
},
/// Write a memory note under the [`crate::fileguard::FileGuard`] (cadrage C7).
/// Memory is project-shared; written directly under a write-lease.
WriteMemory {
@ -345,6 +356,10 @@ impl OrchestratorRequest {
slug: self.optional_slug(),
requester: self.requester_party(),
}),
"skill.read" => Ok(OrchestratorCommand::ReadSkill {
name: self.require_name(action)?,
requester: self.requester_party(),
}),
"memory.write" => Ok(OrchestratorCommand::WriteMemory {
slug: self.require("slug", action, self.slug.as_deref())?,
content: self.require("content", action, self.content.as_deref())?,