#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.
157 lines
4.2 KiB
Rust
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)
|
|
}
|
|
}
|