feat(skill-awareness): T+ — brief « capacités IdeA » inconditionnel dans le contexte d'agent
Enrichit `compose_convention_file` pour que TOUT agent neuf, de tout projet, soit briefé sur l'ensemble des capacités IdeA — plus seulement la délégation. Briefing à haute altitude, télégraphique, injecté à chaque lancement (sobriété token : capacité exposée, jamais le contenu). Les 3 capacités sont décrites sur les 2 surfaces, strictement cloisonnées : - MCP : via les outils `idea_context_read`/`_propose`/`idea_update_context` (contexte projet single-writer, proposition globale enregistrée pour validation, pas auto-appliquée), `idea_memory_read`/`_write` (mémoire durable partagée), `idea_skill_read`/`idea_create_skill` (skills). - Sans MCP : mêmes concepts via les seuls fichiers `.ideai/` (CONTEXT.md, memory/ + MEMORY.md, skills `.md`), aucun nom d'outil `idea_*` ne fuit. But : un agent ne réinvente pas son propre contexte/mémoire/workflow alors qu'IdeA les fournit déjà. 5 tests dédiés (briefing inconditionnel même à vide, honnêteté single-writer du contexte, cloisonnement des surfaces, ordre brief-avant-persona). 1 assert existant adapté (`..._without_skills_omits_section` : `idea_skill_read` est désormais toujours évoqué par le brief ; l'intention reste gardée par l'assert sur l'absence de la section `# Skills disponibles`). `cargo test -p application` = 0 failed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -2624,6 +2624,33 @@ pub(crate) fn compose_convention_file(
|
||||
résultat. Ne réponds **jamais** uniquement en texte : seul `idea_reply` \
|
||||
débloque l'agent qui t'a sollicité.\n\n",
|
||||
);
|
||||
// Capacités IdeA (feature skill-awareness) : briefing à HAUTE ALTITUDE,
|
||||
// télégraphique, injecté à CHAQUE lancement (coût token → pas d'exemples ni de
|
||||
// schémas). On expose la CAPACITÉ (jamais le contenu) : contexte projet géré par
|
||||
// IdeA, mémoire durable partagée, skills. But : un agent neuf ne réinvente pas son
|
||||
// propre contexte/mémoire/workflow alors qu'IdeA les fournit déjà.
|
||||
out.push_str(
|
||||
"**Contexte projet** : le contexte projet d'IdeA EST le contexte du projet — \
|
||||
n'improvise JAMAIS ton propre fichier de contexte/config. Lis-le avec \
|
||||
`idea_context_read` (sans `target`). Pour le faire évoluer, soumets une \
|
||||
proposition via `idea_context_propose` : le contexte projet global est \
|
||||
single-writer (réservé à l'orchestrateur), donc une proposition sans `target` \
|
||||
n'est PAS appliquée directement, elle est enregistrée pour validation. Pour \
|
||||
écrire le `.md` d'un agent précis : `idea_update_context` (ou \
|
||||
`idea_context_propose` avec `target`).\n\n",
|
||||
);
|
||||
out.push_str(
|
||||
"**Mémoire durable projet** : partagée entre TOUS les agents du projet. Lis-la \
|
||||
avec `idea_memory_read` (sans `slug` = index agrégé, ou un slug pour une note), \
|
||||
écris une note stable avec `idea_memory_write`. N'invente pas ton propre \
|
||||
stockage de mémoire.\n\n",
|
||||
);
|
||||
out.push_str(
|
||||
"**Skills** : workflows réutilisables. Ceux qui te sont assignés te sont listés \
|
||||
plus haut dans ce fichier ; charge le détail d'un skill avec \
|
||||
`idea_skill_read(name=…)` ; crée-en un avec `idea_create_skill`. Ne réinvente \
|
||||
pas un workflow déjà couvert par un skill.\n\n",
|
||||
);
|
||||
} else {
|
||||
out.push_str(
|
||||
"Pour déléguer une tâche à un autre agent, n'utilise jamais les subagents \
|
||||
@ -2631,6 +2658,23 @@ pub(crate) fn compose_convention_file(
|
||||
`.ideai/requests/<ton-agent>/` ; IdeA lancera ou réattachera l'agent cible \
|
||||
avec son propre AI Profile, son contexte et sa mémoire.\n\n",
|
||||
);
|
||||
// Capacités IdeA, surface SANS MCP : mêmes concepts décrits UNIQUEMENT via les
|
||||
// fichiers/dossiers `.ideai/` — aucun nom d'outil `idea_*` ici (cloisonnement
|
||||
// strict des deux surfaces). Télégraphique, capacité (jamais contenu).
|
||||
out.push_str(
|
||||
"Le contexte projet partagé vit dans `.ideai/CONTEXT.md` (géré par IdeA) — ne \
|
||||
crée pas ton propre fichier de contexte/config, c'est CELUI-LÀ le contexte du \
|
||||
projet.\n\n",
|
||||
);
|
||||
out.push_str(
|
||||
"La mémoire durable partagée entre tous les agents vit dans `.ideai/memory/` \
|
||||
(notes `.md` + index `MEMORY.md`). N'invente pas ton propre stockage de \
|
||||
mémoire.\n\n",
|
||||
);
|
||||
out.push_str(
|
||||
"Les skills sont des workflows réutilisables, stockés comme des `.md` gérés par \
|
||||
IdeA. Ne réinvente pas un workflow déjà couvert par un skill.\n\n",
|
||||
);
|
||||
}
|
||||
out.push_str(skill_awareness(mcp_enabled));
|
||||
out.push_str(memory_awareness());
|
||||
@ -3011,7 +3055,10 @@ mod tests {
|
||||
);
|
||||
|
||||
// The affordance section is present.
|
||||
assert!(doc.contains("# Skills disponibles"), "MCP skills section present");
|
||||
assert!(
|
||||
doc.contains("# Skills disponibles"),
|
||||
"MCP skills section present"
|
||||
);
|
||||
assert!(doc.contains("idea_skill_read"), "points to the read tool");
|
||||
// Affordance lines: `**name** — <effective description>`.
|
||||
assert!(doc.contains("**refactor** — Refactors code"));
|
||||
@ -3033,10 +3080,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn compose_convention_file_mcp_mode_without_skills_omits_section() {
|
||||
// MCP mode + zero skills ⇒ no `# Skills disponibles` section at all.
|
||||
// MCP mode + zero skills ⇒ no `# Skills disponibles` affordance section at all.
|
||||
// NB : l'outil `idea_skill_read` reste évoqué par le briefing « capacités IdeA »
|
||||
// (toujours présent), donc on n'asserte plus son absence — seulement celle de la
|
||||
// section d'affordances, qui, elle, dépend bien des skills assignés.
|
||||
let doc = compose_convention_file("/root", "", "# Persona", &[], &[], None, true);
|
||||
assert!(!doc.contains("# Skills disponibles"));
|
||||
assert!(!doc.contains("idea_skill_read"));
|
||||
}
|
||||
|
||||
/// Builds a memory index entry for the convention-file composition tests.
|
||||
@ -3202,6 +3251,158 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compose_convention_file_mcp_briefs_all_three_capabilities_even_when_empty() {
|
||||
// Cœur du besoin (feature skill-awareness) : le briefing « capacités IdeA »
|
||||
// est INCONDITIONNEL. Projet/agent neuf et vide (zéro skill, zéro mémoire,
|
||||
// contexte vide) ⇒ les 3 capacités (contexte, mémoire, skills) DOIVENT quand
|
||||
// même être décrites, avec les noms des outils MCP correspondants.
|
||||
let doc = compose_convention_file("/root", "", "# Persona", &[], &[], None, true);
|
||||
|
||||
// Capacité contexte projet.
|
||||
assert!(
|
||||
doc.contains("idea_context_read"),
|
||||
"brief must surface idea_context_read even with empty content"
|
||||
);
|
||||
assert!(doc.contains("idea_context_propose"));
|
||||
assert!(
|
||||
doc.contains("idea_update_context"),
|
||||
"brief must surface idea_update_context for per-agent .md writes"
|
||||
);
|
||||
// Capacité mémoire durable partagée.
|
||||
assert!(doc.contains("idea_memory_read"));
|
||||
assert!(doc.contains("idea_memory_write"));
|
||||
// Capacité skills.
|
||||
assert!(doc.contains("idea_skill_read"));
|
||||
assert!(doc.contains("idea_create_skill"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compose_convention_file_mcp_context_brief_is_single_writer_honest() {
|
||||
// La prose contexte ne doit PAS laisser croire qu'une proposition globale est
|
||||
// appliquée directement. On asserte sur la nuance réellement écrite par le dev :
|
||||
// « single-writer » + le fait qu'une proposition sans `target` est enregistrée
|
||||
// « pour validation » (et donc « n'est PAS appliquée directement »).
|
||||
let doc = compose_convention_file("/root", "", "# Persona", &[], &[], None, true);
|
||||
|
||||
assert!(
|
||||
doc.contains("single-writer"),
|
||||
"context brief must name the single-writer constraint"
|
||||
);
|
||||
assert!(
|
||||
doc.contains("validation"),
|
||||
"context brief must say a proposal is recorded for validation"
|
||||
);
|
||||
assert!(
|
||||
doc.contains("n'est PAS appliquée directement"),
|
||||
"context brief must be honest that a global proposal isn't auto-applied"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compose_convention_file_non_mcp_briefs_capabilities_via_ideai_files_no_tools() {
|
||||
// Surface SANS MCP : mêmes 3 capacités décrites UNIQUEMENT via les fichiers
|
||||
// `.ideai/` — et AUCUN nom d'outil `idea_*` (cloisonnement strict des surfaces).
|
||||
let doc = compose_convention_file("/root", "", "# Persona", &[], &[], None, false);
|
||||
|
||||
// Capacités décrites par leurs fichiers/dossiers.
|
||||
assert!(
|
||||
doc.contains(".ideai/CONTEXT.md"),
|
||||
"non-MCP brief must point to the .ideai/CONTEXT.md context file"
|
||||
);
|
||||
assert!(
|
||||
doc.contains(".ideai/memory/"),
|
||||
"non-MCP brief must point to the .ideai/memory/ store"
|
||||
);
|
||||
assert!(
|
||||
doc.contains("MEMORY.md"),
|
||||
"non-MCP brief must name the MEMORY.md index"
|
||||
);
|
||||
|
||||
// AUCUN nom d'outil `idea_*` ne fuit (asserts forts sur les noms d'OUTILS, pas
|
||||
// sur la sous-chaîne brute `idea` qui matcherait `.ideai/`).
|
||||
assert!(
|
||||
!doc.contains("idea_context"),
|
||||
"no idea_context* tool in file surface"
|
||||
);
|
||||
assert!(
|
||||
!doc.contains("idea_memory"),
|
||||
"no idea_memory* tool in file surface"
|
||||
);
|
||||
assert!(
|
||||
!doc.contains("idea_skill"),
|
||||
"no idea_skill* tool in file surface"
|
||||
);
|
||||
assert!(
|
||||
!doc.contains("idea_create"),
|
||||
"no idea_create* tool in file surface"
|
||||
);
|
||||
assert!(
|
||||
!doc.contains("idea_ask_agent"),
|
||||
"no idea_ask_agent in file surface"
|
||||
);
|
||||
assert!(
|
||||
!doc.contains("idea_update_context"),
|
||||
"no idea_update_context in file surface"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compose_convention_file_surfaces_stay_cloistered_on_capabilities() {
|
||||
// Cloisonnement : la prose MCP ne mentionne pas le protocole fichier
|
||||
// `.ideai/requests`, et la prose fichier ne mentionne aucun nom d'outil.
|
||||
let mcp = compose_convention_file("/root", "", "# Persona", &[], &[], None, true);
|
||||
let file = compose_convention_file("/root", "", "# Persona", &[], &[], None, false);
|
||||
|
||||
assert!(
|
||||
!mcp.contains(".ideai/requests"),
|
||||
"MCP capability brief must not leak the file delegation protocol"
|
||||
);
|
||||
// Aucun nom d'outil `idea_*` dans la surface fichier (recoupe le test dédié,
|
||||
// gardé vert ici sur l'ensemble des outils des 3 capacités + délégation).
|
||||
for tool in [
|
||||
"idea_context_read",
|
||||
"idea_context_propose",
|
||||
"idea_update_context",
|
||||
"idea_memory_read",
|
||||
"idea_memory_write",
|
||||
"idea_skill_read",
|
||||
"idea_create_skill",
|
||||
"idea_ask_agent",
|
||||
"idea_reply",
|
||||
] {
|
||||
assert!(
|
||||
!file.contains(tool),
|
||||
"non-MCP surface must not mention the tool {tool}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compose_convention_file_capability_brief_precedes_the_persona_in_both_modes() {
|
||||
// Ordre : le briefing capacités apparaît AVANT le persona dans les 2 modes.
|
||||
// On ancre le brief sur un marqueur stable propre à chaque surface.
|
||||
let mcp = compose_convention_file("/root", "", "# Persona", &[], &[], None, true);
|
||||
let mcp_brief_at = mcp
|
||||
.find("idea_context_read")
|
||||
.expect("MCP capability brief present");
|
||||
let mcp_persona_at = mcp.find("# Persona").expect("persona present");
|
||||
assert!(
|
||||
mcp_brief_at < mcp_persona_at,
|
||||
"MCP capability brief must come before the persona"
|
||||
);
|
||||
|
||||
let file = compose_convention_file("/root", "", "# Persona", &[], &[], None, false);
|
||||
let file_brief_at = file
|
||||
.find(".ideai/CONTEXT.md")
|
||||
.expect("file capability brief present");
|
||||
let file_persona_at = file.find("# Persona").expect("persona present");
|
||||
assert!(
|
||||
file_brief_at < file_persona_at,
|
||||
"file capability brief must come before the persona"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mcp_declaration_with_runtime_points_the_bridge_at_the_real_endpoint() {
|
||||
// B-0 — a PTY-launched MCP-capable CLI (Claude/Codex REPL) auto-discovers the
|
||||
|
||||
Reference in New Issue
Block a user