Files
IdeA/crates/domain/src/ids.rs
Blomios 7007b2cc7e feat(backend,frontend): tâches planifiées projet — tool MCP idea_scheduled_task_* et menu utilisateur #305, fix transcript lié indisponible après reboot #314 (QA verte)
#305: nouvel agrégat durable ScheduledProjectTask dans domain (scheduled_task.rs, ids, ports ScheduledProjectTaskStore), source de vérité <app_data_dir>/scheduled-project-tasks/<projectId>.json, schedules bornés V1 interval/daily/weekly avec fuseau, cycle À approuver/Active/Suspendue avec approbation obligatoire des tâches demandées par agent; use cases application/scheduled_task (list/create/update/delete/run_now/reconcile/fire) réutilisant BackgroundTask pour les runs avec distinction planifié/manuel; store infrastructure dédié + scheduler reconcile au boot sans rattrapage en rafale, cwd confiné au projet; 7 tools MCP idea_scheduled_task_* (list/read lecture seule, create/update/delete/run_now/set_enabled write-actions soumises aux permissions natives); commandes Tauri + routes HTTP aux contrats DTO identiques (camelCase nextRunAtMs/lastRunAtMs/lastTaskId/createdBy/updatedBy); frontend: gateways Tauri/HTTP/mock + types ports/domain, surface projet 'Tâches planifiées' (liste utilisateur+agent unifiée, création/édition avec récapitulatif avant activation, approbation/refus, exécuter maintenant, suspendre/reprendre, suppression), catalogage surface web (webSurfaceCatalog/viewPlacement/ViewPanelBody/WebWorkspace); docs/scheduled-project-tasks.md et sdk docs/services.md (047c38b: pas un service plugin V1, pas d'émulation via cron système).

#314: correction de 'Conversation liée — transcript indisponible' après reboot: restauration du transcript lié côté application (orchestrator service, lifecycle, session_limit, background sink, terminal registry) et côté frontend (adapters agent/http frames/stream/requestResponse, workStateNormalization, CustomAgentChatView avec état et action utile), tests dto_chat/agent_lifecycle/structured_registry_d1/background_completion_sink étendus.

QA verte rapportée par QA sur feature/305-scheduled-project-tasks-mcp, sans bloquant, pour #305 et #314.

Hors périmètre worktree: tickets non liés, attachments/, scripts/; aucun push.
2026-09-11 10:32:01 +02:00

157 lines
4.2 KiB
Rust

//! Strongly-typed identifiers.
//!
//! Each identifier is a `newtype` around [`uuid::Uuid`]. Using distinct types
//! per concept makes it impossible to pass, say, an [`AgentId`] where a
//! [`ProjectId`] is expected (compile-time safety, SOLID/typing discipline).
use serde::{Deserialize, Serialize};
use uuid::Uuid;
macro_rules! typed_id {
($(#[$meta:meta])* $name:ident) => {
$(#[$meta])*
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(transparent)]
pub struct $name(pub Uuid);
impl $name {
/// Wraps an existing [`Uuid`].
#[must_use]
pub const fn from_uuid(id: Uuid) -> Self {
Self(id)
}
/// Generates a fresh random (v4) identifier.
///
/// Prefer injecting an [`crate::ports::IdGenerator`] in application
/// code for determinism; this convenience exists for tests and the
/// composition root.
#[must_use]
pub fn new_random() -> Self {
Self(Uuid::new_v4())
}
/// Returns the inner [`Uuid`].
#[must_use]
pub const fn as_uuid(&self) -> Uuid {
self.0
}
}
impl std::fmt::Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<Uuid> for $name {
fn from(id: Uuid) -> Self {
Self(id)
}
}
};
}
typed_id!(
/// Identifies a [`crate::project::Project`].
ProjectId
);
typed_id!(
/// Identifies an [`crate::agent::Agent`].
AgentId
);
typed_id!(
/// Identifies an [`crate::template::AgentTemplate`].
TemplateId
);
typed_id!(
/// Identifies an [`crate::profile::AgentProfile`].
ProfileId
);
typed_id!(
/// Identifies a local model server configuration.
LocalModelServerId
);
typed_id!(
/// Identifies a [`crate::skill::Skill`].
SkillId
);
typed_id!(
/// Identifies a [`crate::issue::Issue`].
IssueId
);
typed_id!(
/// Identifies a [`crate::sprint::Sprint`].
SprintId
);
typed_id!(
/// Identifies a [`crate::terminal::TerminalSession`].
SessionId
);
typed_id!(
/// Identifies a [`crate::layout::WindowId`]-bearing OS window.
WindowId
);
typed_id!(
/// Identifies a [`crate::layout::Tab`].
TabId
);
typed_id!(
/// Identifies one named terminal layout within a project (L10/#4).
LayoutId
);
typed_id!(
/// Identifies a node in a [`crate::layout::LayoutTree`].
NodeId
);
typed_id!(
/// Identifies one armed one-shot wake-up of a [`crate::ports::Scheduler`]
/// (ARCHITECTURE §21.4). Opaque, cancellable handle returned by
/// [`crate::ports::Scheduler::arm`] and consumed by
/// [`crate::ports::Scheduler::cancel`].
ScheduleId
);
typed_id!(
/// Identifies a durable project-scoped scheduled task definition.
ScheduledTaskId
);
typed_id!(
/// Identifies a first-class background task.
TaskId
);
typed_id!(
/// Identifies one composite inter-agent business rendezvous.
RendezvousId
);
/// Runtime-only key for an agent scoped by its project.
///
/// `AgentId` is persisted inside a project and is not globally unique across
/// simultaneously opened projects. Any app-wide in-memory registry that tracks
/// live runtime state for an agent must use this key instead of `AgentId` alone.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RuntimeAgentKey {
/// Project that owns the runtime agent.
pub project_id: ProjectId,
/// Agent inside that project.
pub agent_id: AgentId,
}
impl RuntimeAgentKey {
/// Builds a scoped runtime key from its persisted identifiers.
#[must_use]
pub const fn new(project_id: ProjectId, agent_id: AgentId) -> Self {
Self {
project_id,
agent_id,
}
}
}
impl std::fmt::Display for RuntimeAgentKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{}", self.project_id, self.agent_id)
}
}