ticket115: snapshot runtime skills assignés + durcissement idea_skill_read

This commit is contained in:
2026-07-31 13:56:18 +02:00
parent fe5fe7cb70
commit fbe3c51fd4
8 changed files with 611 additions and 215 deletions

View File

@ -169,6 +169,11 @@ pub use memory_harvest::{
MAX_BLOCK_BYTES, MAX_DESCRIPTION_CHARS,
};
pub use orchestrator::{
AssignedSkillSnapshot, OrchestrationCapabilitySnapshot,
ORCHESTRATION_CAPABILITY_SNAPSHOT_VERSION,
};
pub use model_catalogue::{
evaluate_compatibility, CliVersion, CompatibilityMatrix, ModelCatalogSource,
ModelCatalogueError, ModelCompatibility,

View File

@ -14,11 +14,65 @@
use serde::{Deserialize, Serialize};
use crate::conversation::ConversationParty;
use crate::ids::{AgentId, NodeId};
use crate::ids::{AgentId, NodeId, SkillId};
use crate::live_state::WorkStatus;
use crate::mailbox::TicketId;
use crate::skill::SkillScope;
/// Current schema version for orchestration capabilities injected into an
/// agent's effective runtime context.
pub const ORCHESTRATION_CAPABILITY_SNAPSHOT_VERSION: u32 = 1;
/// Runtime snapshot of one skill assigned to an agent.
///
/// This is the agent-facing capability contract: launch context rendering and
/// `idea_skill_read` authorization are both derived from these snapshots, not
/// from ad hoc skill-name lookups.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AssignedSkillSnapshot {
/// Stable skill id from the selected scope.
pub skill_id: SkillId,
/// Store scope used to resolve the skill body.
pub scope: SkillScope,
/// Display name shown to the model and accepted by `idea_skill_read`.
pub name: String,
/// One-line affordance description shown in the model context.
pub description: String,
/// Position in the agent assignment list, preserving manifest order.
pub assignment_index: u32,
}
/// Provider-agnostic orchestration capability snapshot for one agent at runtime.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OrchestrationCapabilitySnapshot {
/// Snapshot schema version.
pub version: u32,
/// Skills assigned to the agent and resolved for this runtime.
pub assigned_skills: Vec<AssignedSkillSnapshot>,
}
impl OrchestrationCapabilitySnapshot {
/// Builds an empty snapshot at the current schema version.
#[must_use]
pub const fn empty() -> Self {
Self {
version: ORCHESTRATION_CAPABILITY_SNAPSHOT_VERSION,
assigned_skills: Vec::new(),
}
}
/// Builds a snapshot from assigned skills.
#[must_use]
pub fn new(assigned_skills: Vec<AssignedSkillSnapshot>) -> Self {
Self {
version: ORCHESTRATION_CAPABILITY_SNAPSHOT_VERSION,
assigned_skills,
}
}
}
/// Errors raised while validating a raw [`OrchestratorRequest`].
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum OrchestratorError {
@ -293,15 +347,15 @@ 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).
/// Read a reusable skill's Markdown body **by name** (`idea_skill_read`).
/// The application layer authorizes the read against the requester's assigned
/// runtime skill snapshot; unassigned skills are not readable. 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.
/// The party that issued the read (handshake identity). Must be an agent
/// requester for authorization against assigned skills.
requester: ConversationParty,
},
/// Write a memory note under the [`crate::fileguard::FileGuard`] (cadrage C7).