feat(sprints): modèle de sprints — domaine, use-cases, persistance et surfaces (backend)
Ticket #10 — introduction du modèle de sprints côté backend. - Domaine : nouvel agrégat Sprint (sprint.rs), IDs, événements et invariants ; rattachement des issues à un sprint (issue.rs) et ports associés. - Application : use-cases sprints (application/src/sprints) + erreurs dédiées. - Infrastructure : store de sprints (infrastructure/src/sprints.rs), adaptation du store d'issues et exposition MCP via orchestrator/mcp/tickets.rs. - app-tauri : commandes, state et events pour piloter les sprints depuis l'UI. Tests domaine/application/infra/app-tauri verts (sprint_usecases, sprint_store, issue_store, mcp_server). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -32,7 +32,7 @@ use crate::background_task::{
|
||||
BackgroundTask, BackgroundTaskKind, BackgroundTaskResult, BackgroundTaskWakePolicy,
|
||||
};
|
||||
use crate::events::DomainEvent;
|
||||
use crate::ids::{AgentId, NodeId, ProjectId, ScheduleId, SessionId, TaskId};
|
||||
use crate::ids::{AgentId, NodeId, ProjectId, ScheduleId, SessionId, SprintId, TaskId};
|
||||
use crate::issue::{
|
||||
Issue, IssueCarnet, IssueIndexEntry, IssueListFilter, IssueNumber, IssueRef, IssueVersion,
|
||||
};
|
||||
@ -43,6 +43,7 @@ use crate::profile::{AgentProfile, EmbedderProfile};
|
||||
use crate::project::{Project, ProjectPath};
|
||||
use crate::remote::RemoteKind;
|
||||
use crate::skill::{Skill, SkillScope};
|
||||
use crate::sprint::{Sprint, SprintIndexEntry, SprintVersion};
|
||||
use crate::template::AgentTemplate;
|
||||
use crate::terminal::PtySize;
|
||||
|
||||
@ -596,6 +597,28 @@ pub enum IssueStoreError {
|
||||
#[error("issue store failed: {0}")]
|
||||
Store(String),
|
||||
}
|
||||
|
||||
/// Errors from the sprint store.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Error)]
|
||||
pub enum SprintStoreError {
|
||||
/// The requested sprint does not exist.
|
||||
#[error("sprint not found")]
|
||||
NotFound,
|
||||
/// Optimistic concurrency conflict.
|
||||
#[error("sprint version conflict: expected {expected}, actual {actual}")]
|
||||
VersionConflict {
|
||||
/// Expected version.
|
||||
expected: SprintVersion,
|
||||
/// Actual persisted version.
|
||||
actual: SprintVersion,
|
||||
},
|
||||
/// The sprint payload is invalid.
|
||||
#[error("sprint invalid: {0}")]
|
||||
Invalid(String),
|
||||
/// Store I/O or serialization failed.
|
||||
#[error("sprint store failed: {0}")]
|
||||
Store(String),
|
||||
}
|
||||
// ---------------------------------------------------------------------------
|
||||
// Git port support types
|
||||
// ---------------------------------------------------------------------------
|
||||
@ -1507,6 +1530,50 @@ pub trait IssueStore: Send + Sync {
|
||||
) -> Result<IssueCarnet, IssueStoreError>;
|
||||
}
|
||||
|
||||
/// Persistence port for project-scoped sprints.
|
||||
#[async_trait]
|
||||
pub trait SprintStore: Send + Sync {
|
||||
/// Creates a new sprint.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`SprintStoreError`] when the sprint already exists or cannot be stored.
|
||||
async fn create(&self, root: &ProjectPath, sprint: &Sprint) -> Result<(), SprintStoreError>;
|
||||
|
||||
/// Reads a sprint by stable id.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`SprintStoreError::NotFound`] when absent.
|
||||
async fn get(
|
||||
&self,
|
||||
root: &ProjectPath,
|
||||
sprint_id: SprintId,
|
||||
) -> Result<Sprint, SprintStoreError>;
|
||||
|
||||
/// Lists sprint index entries.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`SprintStoreError`] on persistence failure.
|
||||
async fn list(&self, root: &ProjectPath) -> Result<Vec<SprintIndexEntry>, SprintStoreError>;
|
||||
|
||||
/// Replaces a sprint after checking its expected version.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`SprintStoreError::VersionConflict`] on optimistic-concurrency conflict.
|
||||
async fn update(
|
||||
&self,
|
||||
root: &ProjectPath,
|
||||
sprint: &Sprint,
|
||||
expected_version: SprintVersion,
|
||||
) -> Result<(), SprintStoreError>;
|
||||
|
||||
/// Deletes a sprint by id.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`SprintStoreError::NotFound`] when absent.
|
||||
async fn delete(&self, root: &ProjectPath, sprint_id: SprintId)
|
||||
-> Result<(), SprintStoreError>;
|
||||
}
|
||||
|
||||
/// Git operations for a project. Named `GitPort` to avoid clashing with the
|
||||
/// [`crate::git::GitRepository`] *entity* (state image).
|
||||
#[async_trait]
|
||||
|
||||
Reference in New Issue
Block a user