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.
This commit is contained in:
2026-09-11 10:32:01 +02:00
parent 72792bb02b
commit 7007b2cc7e
63 changed files with 3887 additions and 100 deletions

View File

@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::conversation::ConversationId;
use crate::ids::{AgentId, ProjectId, RendezvousId, ScheduleId, TaskId};
use crate::ids::{AgentId, ProjectId, RendezvousId, ScheduleId, ScheduledTaskId, TaskId};
use crate::mailbox::TicketId;
/// Maximum length for human-facing task labels.
@ -63,6 +63,26 @@ pub enum BackgroundTaskWakePolicy {
RecordOnly,
}
/// Provenance of one concrete run.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(
rename_all = "camelCase",
rename_all_fields = "camelCase",
tag = "kind"
)]
pub enum BackgroundTaskOrigin {
/// Direct/manual launch or legacy task.
#[default]
Manual,
/// Occurrence of a durable scheduled definition.
Scheduled {
/// Durable definition that produced this run.
scheduled_task_id: ScheduledTaskId,
/// Persisted occurrence timestamp represented by this run.
planned_at_ms: u64,
},
}
/// Structural link from a background task to a composite inter-agent rendezvous.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
@ -137,6 +157,9 @@ pub struct BackgroundTask {
pub state: BackgroundTaskState,
/// Completion wake policy.
pub wake_policy: BackgroundTaskWakePolicy,
/// Manual launch or scheduled occurrence provenance.
#[serde(default)]
pub origin: BackgroundTaskOrigin,
/// Creation timestamp, epoch milliseconds.
pub created_at_ms: u64,
/// Last update timestamp, epoch milliseconds.
@ -173,6 +196,7 @@ impl BackgroundTask {
parent_task_id: None,
state: BackgroundTaskState::Queued,
wake_policy,
origin: BackgroundTaskOrigin::Manual,
created_at_ms: now_ms,
updated_at_ms: now_ms,
deadline_ms,
@ -183,6 +207,13 @@ impl BackgroundTask {
Ok(task)
}
/// Attaches immutable run provenance before persistence.
#[must_use]
pub fn with_origin(mut self, origin: BackgroundTaskOrigin) -> Self {
self.origin = origin;
self
}
/// Returns a copy linked to a composite inter-agent rendezvous.
#[must_use]
pub fn with_rendezvous(mut self, link: BackgroundTaskRendezvousLink) -> Self {