feat(attachments): fondation pipeline durable d'attachments agent/chat + sandbox-safe (#154, QA verte)

Socle #154 : entité attachment, store, contrat DTO et routage session. Modules : domain/chat_attachment, application/chat_attachments, infrastructure/chat_attachments, app-tauri (commands/lib), backend dto.
This commit is contained in:
2026-08-06 10:44:15 +02:00
parent fb19ee48dc
commit 1c80d08f92
16 changed files with 1210 additions and 158 deletions

View File

@ -4,7 +4,8 @@
mod helpers;
use domain::{
Agent, AgentManifest, AgentOrigin, AgentProfile, AgentTemplate, ContextInjection, DomainError,
Agent, AgentManifest, AgentOrigin, AgentProfile, AgentTemplate, ChatAttachment,
ChatAttachmentError, ChatAttachmentId, ChatAttachmentSourceKind, ContextInjection, DomainError,
ManifestEntry, MarkdownDoc, ProfileId, Project, ProjectPath, PtySize, RemoteRef,
SessionStrategy, Skill, SkillId, SkillRef, SkillScope, SshAuth, TemplateId, TemplateVersion,
};
@ -19,6 +20,23 @@ fn template_id() -> TemplateId {
TemplateId::from_uuid(Uuid::from_u128(7))
}
fn chat_attachment() -> ChatAttachment {
ChatAttachment {
id: ChatAttachmentId::new("attach-1").unwrap(),
project_id: domain::ProjectId::from_uuid(Uuid::from_u128(1)),
agent_id: domain::AgentId::from_uuid(Uuid::from_u128(2)),
session_id: domain::SessionId::from_uuid(Uuid::from_u128(3)),
filename: "note.txt".to_owned(),
mime: "text/plain".to_owned(),
size_bytes: 12,
source_kind: ChatAttachmentSourceKind::LocalFile,
storage_path: "agent-chat/session/attach-1-note.txt".to_owned(),
readable_path: "/project/.ideai/attachments/agent-chat/session/attach-1-note.txt"
.to_owned(),
created_at: 123,
}
}
// ---------------------------------------------------------------------------
// ProjectPath
// ---------------------------------------------------------------------------
@ -52,6 +70,26 @@ fn project_path_rejects_empty() {
assert!(matches!(err, DomainError::EmptyField { .. }));
}
// ---------------------------------------------------------------------------
// Chat attachments
// ---------------------------------------------------------------------------
#[test]
fn chat_attachment_metadata_accepts_agent_chat_storage_path() {
assert!(chat_attachment().validate().is_ok());
}
#[test]
fn chat_attachment_metadata_rejects_storage_path_outside_agent_chat_namespace() {
let mut attachment = chat_attachment();
attachment.storage_path = "tickets/1/file.txt".to_owned();
assert!(matches!(
attachment.validate(),
Err(ChatAttachmentError::InvalidStoragePath(_))
));
}
// ---------------------------------------------------------------------------
// Project (also exercises the Clock/IdGenerator port fakes for determinism)
// ---------------------------------------------------------------------------