feat(tickets): checkpoint backend V1 du système de tickets (T1-T5)
Checkpoint de travail — QA formelle encore à venir (après reset Codex). `cargo build` workspace OK, tests ticket/issue verts (domaine, store, use cases, app-tauri 47/51). Le domaine s'appelle `Issue`, exposé « ticket » côté MCP/UI. - T1 domaine : entité Issue (statut, priorité, liens, carnet), events, ids, ports. - T2 infra : store FS Markdown + allocator de références #N. - T3 application : use cases Issue (create/read/list/update/status/ priority/carnet/link/unlink/assign) + erreurs dédiées. - T4 surface MCP : 10 outils publics idea_ticket_* (23 outils au total), mappés vers les use cases Issue. - T5 app-tauri : commandes Tauri ticket_* + DTOs d'events Issue + câblage state.rs. Dette de test PRÉ-EXISTANTE héritée de develop (4 tests app-tauri mcp_e2e_loopback / mcp_serve_peer rouges) hors périmètre tickets, non traitée ici. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -14,6 +14,7 @@ use tauri::{AppHandle, Emitter};
|
||||
|
||||
use domain::events::{DomainEvent, OrchestrationSource};
|
||||
use domain::input::AgentLiveness;
|
||||
use domain::{IssueLinkKind, IssuePriority, IssueStatus};
|
||||
use infrastructure::TokioBroadcastEventBus;
|
||||
|
||||
/// Name of the Tauri event carrying relayed [`DomainEvent`]s.
|
||||
@ -158,6 +159,94 @@ pub enum DomainEventDto {
|
||||
/// Project id.
|
||||
project_id: String,
|
||||
},
|
||||
/// An issue-backed public ticket was created.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
IssueCreated {
|
||||
/// Issue id.
|
||||
issue_id: String,
|
||||
/// Public ticket reference (`#N`).
|
||||
issue_ref: String,
|
||||
},
|
||||
/// An issue-backed public ticket was updated.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
IssueUpdated {
|
||||
/// Public ticket reference (`#N`).
|
||||
issue_ref: String,
|
||||
/// New optimistic version.
|
||||
version: u64,
|
||||
},
|
||||
/// A public ticket status changed.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
IssueStatusChanged {
|
||||
/// Public ticket reference (`#N`).
|
||||
issue_ref: String,
|
||||
/// New status.
|
||||
status: String,
|
||||
/// New optimistic version.
|
||||
version: u64,
|
||||
},
|
||||
/// A public ticket priority changed.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
IssuePriorityChanged {
|
||||
/// Public ticket reference (`#N`).
|
||||
issue_ref: String,
|
||||
/// New priority.
|
||||
priority: String,
|
||||
/// New optimistic version.
|
||||
version: u64,
|
||||
},
|
||||
/// A public ticket carnet changed.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
IssueCarnetUpdated {
|
||||
/// Public ticket reference (`#N`).
|
||||
issue_ref: String,
|
||||
/// New optimistic version.
|
||||
version: u64,
|
||||
},
|
||||
/// A public ticket link was added.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
IssueLinked {
|
||||
/// Public source ticket reference (`#N`).
|
||||
issue_ref: String,
|
||||
/// Public target ticket reference (`#N`).
|
||||
target: String,
|
||||
/// Link kind.
|
||||
kind: String,
|
||||
/// New optimistic version.
|
||||
version: u64,
|
||||
},
|
||||
/// A public ticket link was removed.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
IssueUnlinked {
|
||||
/// Public source ticket reference (`#N`).
|
||||
issue_ref: String,
|
||||
/// Public target ticket reference (`#N`).
|
||||
target: String,
|
||||
/// Link kind.
|
||||
kind: String,
|
||||
/// New optimistic version.
|
||||
version: u64,
|
||||
},
|
||||
/// An agent was assigned to a public ticket.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
IssueAgentAssigned {
|
||||
/// Public ticket reference (`#N`).
|
||||
issue_ref: String,
|
||||
/// Assigned agent id.
|
||||
agent_id: String,
|
||||
/// New optimistic version.
|
||||
version: u64,
|
||||
},
|
||||
/// An agent was unassigned from a public ticket.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
IssueAgentUnassigned {
|
||||
/// Public ticket reference (`#N`).
|
||||
issue_ref: String,
|
||||
/// Unassigned agent id.
|
||||
agent_id: String,
|
||||
/// New optimistic version.
|
||||
version: u64,
|
||||
},
|
||||
/// An orchestrator request was processed on behalf of a requester agent.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
OrchestratorRequestProcessed {
|
||||
@ -312,6 +401,34 @@ impl From<OrchestrationSource> for OrchestrationSourceDto {
|
||||
}
|
||||
}
|
||||
|
||||
fn issue_status_wire(status: IssueStatus) -> &'static str {
|
||||
match status {
|
||||
IssueStatus::Open => "open",
|
||||
IssueStatus::InProgress => "inProgress",
|
||||
IssueStatus::Qa => "QA",
|
||||
IssueStatus::Closed => "closed",
|
||||
}
|
||||
}
|
||||
|
||||
fn issue_priority_wire(priority: IssuePriority) -> &'static str {
|
||||
match priority {
|
||||
IssuePriority::Low => "low",
|
||||
IssuePriority::Medium => "medium",
|
||||
IssuePriority::High => "high",
|
||||
IssuePriority::Critical => "critical",
|
||||
}
|
||||
}
|
||||
|
||||
fn issue_link_kind_wire(kind: IssueLinkKind) -> &'static str {
|
||||
match kind {
|
||||
IssueLinkKind::RelatesTo => "relatesTo",
|
||||
IssueLinkKind::Blocks => "blocks",
|
||||
IssueLinkKind::BlockedBy => "blockedBy",
|
||||
IssueLinkKind::Duplicates => "duplicates",
|
||||
IssueLinkKind::DependsOn => "dependsOn",
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&DomainEvent> for DomainEventDto {
|
||||
fn from(e: &DomainEvent) -> Self {
|
||||
match e {
|
||||
@ -400,6 +517,79 @@ impl From<&DomainEvent> for DomainEventDto {
|
||||
DomainEvent::GitStateChanged { project_id } => Self::GitStateChanged {
|
||||
project_id: project_id.to_string(),
|
||||
},
|
||||
DomainEvent::IssueCreated {
|
||||
issue_id,
|
||||
issue_ref,
|
||||
} => Self::IssueCreated {
|
||||
issue_id: issue_id.to_string(),
|
||||
issue_ref: issue_ref.to_string(),
|
||||
},
|
||||
DomainEvent::IssueUpdated { issue_ref, version } => Self::IssueUpdated {
|
||||
issue_ref: issue_ref.to_string(),
|
||||
version: version.get(),
|
||||
},
|
||||
DomainEvent::IssueStatusChanged {
|
||||
issue_ref,
|
||||
status,
|
||||
version,
|
||||
} => Self::IssueStatusChanged {
|
||||
issue_ref: issue_ref.to_string(),
|
||||
status: issue_status_wire(*status).to_owned(),
|
||||
version: version.get(),
|
||||
},
|
||||
DomainEvent::IssuePriorityChanged {
|
||||
issue_ref,
|
||||
priority,
|
||||
version,
|
||||
} => Self::IssuePriorityChanged {
|
||||
issue_ref: issue_ref.to_string(),
|
||||
priority: issue_priority_wire(*priority).to_owned(),
|
||||
version: version.get(),
|
||||
},
|
||||
DomainEvent::IssueCarnetUpdated { issue_ref, version } => Self::IssueCarnetUpdated {
|
||||
issue_ref: issue_ref.to_string(),
|
||||
version: version.get(),
|
||||
},
|
||||
DomainEvent::IssueLinked {
|
||||
issue_ref,
|
||||
target,
|
||||
kind,
|
||||
version,
|
||||
} => Self::IssueLinked {
|
||||
issue_ref: issue_ref.to_string(),
|
||||
target: target.to_string(),
|
||||
kind: issue_link_kind_wire(*kind).to_owned(),
|
||||
version: version.get(),
|
||||
},
|
||||
DomainEvent::IssueUnlinked {
|
||||
issue_ref,
|
||||
target,
|
||||
kind,
|
||||
version,
|
||||
} => Self::IssueUnlinked {
|
||||
issue_ref: issue_ref.to_string(),
|
||||
target: target.to_string(),
|
||||
kind: issue_link_kind_wire(*kind).to_owned(),
|
||||
version: version.get(),
|
||||
},
|
||||
DomainEvent::IssueAgentAssigned {
|
||||
issue_ref,
|
||||
agent_id,
|
||||
version,
|
||||
} => Self::IssueAgentAssigned {
|
||||
issue_ref: issue_ref.to_string(),
|
||||
agent_id: agent_id.to_string(),
|
||||
version: version.get(),
|
||||
},
|
||||
DomainEvent::IssueAgentUnassigned {
|
||||
issue_ref,
|
||||
agent_id,
|
||||
version,
|
||||
} => Self::IssueAgentUnassigned {
|
||||
issue_ref: issue_ref.to_string(),
|
||||
agent_id: agent_id.to_string(),
|
||||
version: version.get(),
|
||||
},
|
||||
DomainEvent::OrchestratorRequestProcessed {
|
||||
requester_id,
|
||||
action,
|
||||
|
||||
@ -21,6 +21,7 @@ pub mod mcp_bridge;
|
||||
pub mod mcp_endpoint;
|
||||
pub mod pty;
|
||||
pub mod state;
|
||||
pub mod tickets;
|
||||
|
||||
use std::process::ExitCode;
|
||||
|
||||
@ -163,6 +164,15 @@ pub fn run() {
|
||||
commands::dismiss_embedder_suggestion,
|
||||
commands::create_agent,
|
||||
commands::list_agents,
|
||||
tickets::ticket_create,
|
||||
tickets::ticket_read,
|
||||
tickets::ticket_list,
|
||||
tickets::ticket_update,
|
||||
tickets::ticket_read_carnet,
|
||||
tickets::ticket_update_carnet,
|
||||
tickets::ticket_link,
|
||||
tickets::ticket_unlink,
|
||||
tickets::ticket_assign,
|
||||
commands::get_project_work_state,
|
||||
commands::read_conversation_page,
|
||||
commands::list_live_agents,
|
||||
|
||||
@ -12,35 +12,36 @@ use std::path::PathBuf;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use application::{
|
||||
AgentResumer, AppError, AssignSkillToAgent, AttachLiveAgent, ChangeAgentProfile,
|
||||
CheckEmbedderSuggestion, CloseProject, CloseTab, CloseTerminal, ConfigureProfiles,
|
||||
ContextGuardUseCases, CreateAgentFromScratch, CreateAgentFromTemplate, CreateLayout,
|
||||
CreateMemory, CreateProject, CreateSkill, CreateTemplate, DeleteAgent, DeleteEmbedderProfile,
|
||||
DeleteLayout, DeleteMemory, DeleteProfile, DeleteSkill, DeleteTemplate,
|
||||
DescribeEmbedderEngines, DetectAgentDrift, DetectProfiles, DismissEmbedderSuggestion,
|
||||
FirstRunState, GetLiveStateLean, GetMemory, GetProjectPermissions, GetProjectWorkState,
|
||||
GitBranches, GitCheckout, GitCommit, GitGraph, GitInit, GitLog, GitStage, GitStatus,
|
||||
GitUnstage, HarvestMemoryFromTurn, HealthUseCase, InspectConversation, LaunchAgent,
|
||||
LaunchAgentInput, ListAgents, ListAgentsInput, ListEmbedderProfiles, ListLayouts, ListMemories,
|
||||
ListProfiles, ListProjects, ListResumableAgents, ListSkills, ListTemplates, LiveAgentRegistry,
|
||||
LiveSessions, LiveStateLeanProvider, LiveStateProvider, LiveStateReadProvider, LoadLayout,
|
||||
McpRuntime, MoveTabToNewWindow, MutateLayout, OnnxModelView, OpenProject, OpenTerminal,
|
||||
OrchestratorService, PermissionProjectorRegistry, ProposeContext, ReadAgentContext,
|
||||
ReadContext, ReadConversationPage, ReadMemory, ReadMemoryIndex, ReadProjectContext, ReadSkill,
|
||||
RecallMemory, ReconcileLayouts, ReconcileLiveState, ReconcileLiveStateInput, RecordTurn,
|
||||
RecordTurnProvider, ReferenceProfiles, RenameLayout, ResizeTerminal, ResolveAgentPermissions,
|
||||
ResolveMemoryLinks, RotateConversationLog, SaveEmbedderProfile, SaveProfile,
|
||||
SessionLimitService, SetActiveLayout, SnapshotRunningAgents, StopLiveAgent, StructuredSessions,
|
||||
SuggestedThisSession, SyncAgentWithTemplate, TerminalSessions, UnassignSkillFromAgent,
|
||||
UpdateAgentContext, UpdateAgentPermissions, UpdateLiveState, UpdateMemory,
|
||||
UpdateProjectContext, UpdateProjectPermissions, UpdateSkill, UpdateTemplate, WriteMemory,
|
||||
WriteToTerminal, AGENT_MEMORY_RECALL_BUDGET,
|
||||
AgentResumer, AppError, AssignIssueAgent, AssignSkillToAgent, AttachLiveAgent,
|
||||
ChangeAgentProfile, CheckEmbedderSuggestion, CloseProject, CloseTab, CloseTerminal,
|
||||
ConfigureProfiles, ContextGuardUseCases, CreateAgentFromScratch, CreateAgentFromTemplate,
|
||||
CreateIssue, CreateLayout, CreateMemory, CreateProject, CreateSkill, CreateTemplate,
|
||||
DeleteAgent, DeleteEmbedderProfile, DeleteLayout, DeleteMemory, DeleteProfile, DeleteSkill,
|
||||
DeleteTemplate, DescribeEmbedderEngines, DetectAgentDrift, DetectProfiles,
|
||||
DismissEmbedderSuggestion, FirstRunState, GetLiveStateLean, GetMemory, GetProjectPermissions,
|
||||
GetProjectWorkState, GitBranches, GitCheckout, GitCommit, GitGraph, GitInit, GitLog, GitStage,
|
||||
GitStatus, GitUnstage, HarvestMemoryFromTurn, HealthUseCase, InspectConversation, LaunchAgent,
|
||||
LaunchAgentInput, LinkIssues, ListAgents, ListAgentsInput, ListEmbedderProfiles, ListIssues,
|
||||
ListLayouts, ListMemories, ListProfiles, ListProjects, ListResumableAgents, ListSkills,
|
||||
ListTemplates, LiveAgentRegistry, LiveSessions, LiveStateLeanProvider, LiveStateProvider,
|
||||
LiveStateReadProvider, LoadLayout, McpRuntime, MoveTabToNewWindow, MutateLayout, OnnxModelView,
|
||||
OpenProject, OpenTerminal, OrchestratorService, PermissionProjectorRegistry, ProposeContext,
|
||||
ReadAgentContext, ReadContext, ReadConversationPage, ReadIssue, ReadIssueCarnet, ReadMemory,
|
||||
ReadMemoryIndex, ReadProjectContext, ReadSkill, RecallMemory, ReconcileLayouts,
|
||||
ReconcileLiveState, ReconcileLiveStateInput, RecordTurn, RecordTurnProvider, ReferenceProfiles,
|
||||
RenameLayout, ResizeTerminal, ResolveAgentPermissions, ResolveMemoryLinks,
|
||||
RotateConversationLog, SaveEmbedderProfile, SaveProfile, SessionLimitService, SetActiveLayout,
|
||||
SnapshotRunningAgents, StopLiveAgent, StructuredSessions, SuggestedThisSession,
|
||||
SyncAgentWithTemplate, TerminalSessions, UnassignSkillFromAgent, UnlinkIssues,
|
||||
UpdateAgentContext, UpdateAgentPermissions, UpdateIssue, UpdateIssueCarnet, UpdateLiveState,
|
||||
UpdateMemory, UpdateProjectContext, UpdateProjectPermissions, UpdateSkill, UpdateTemplate,
|
||||
WriteMemory, WriteToTerminal, AGENT_MEMORY_RECALL_BUDGET,
|
||||
};
|
||||
use domain::ports::{
|
||||
AgentContextStore, AgentRuntime, AgentSessionFactory, Clock, Embedder, EmbedderEnvInspector,
|
||||
EmbedderProfileStore, EmbedderPromptStore, EventBus, FileSystem, GitPort, IdGenerator,
|
||||
MemoryRecall, MemoryStore, PermissionStore, ProcessSpawner, ProfileStore, ProjectStore,
|
||||
PtyPort, ScheduledTask, Scheduler, SkillStore, TemplateStore,
|
||||
IssueNumberAllocator, IssueStore, MemoryRecall, MemoryStore, PermissionStore, ProcessSpawner,
|
||||
ProfileStore, ProjectStore, PtyPort, ScheduledTask, Scheduler, SkillStore, TemplateStore,
|
||||
};
|
||||
use domain::profile::{
|
||||
AgentProfile, ContextInjection, McpConfigStrategy, McpTransport, StructuredAdapter,
|
||||
@ -54,19 +55,21 @@ use infrastructure::{
|
||||
embedder_from_profile, AdaptiveMemoryRecall, ClaudePermissionProjector,
|
||||
ClaudeTranscriptInspector, CliAgentRuntime, CodexPermissionProjector, EmbedderEnvProbe,
|
||||
FsConversationLog, FsEmbedderProfileStore, FsEmbedderPromptStore, FsHandoffStore,
|
||||
FsLiveStateStore, FsMemoryStore, FsOrchestratorWatcher, FsPermissionStore, FsProfileStore,
|
||||
FsProjectStore, FsProviderSessionStore, FsSkillStore, FsTemplateStore, Git2Repository,
|
||||
HeuristicHandoffSummarizer, IdeaiContextStore, InMemoryConversationRegistry, InMemoryMailbox,
|
||||
LocalFileSystem, LocalProcessSpawner, McpServer, MediatedInbox, NaiveMemoryRecall,
|
||||
OrchestratorWatchHandle, PortablePtyAdapter, RwFileGuard, StructuredSessionFactory,
|
||||
SystemClock, SystemMillisClock, TokioBroadcastEventBus, TokioScheduler, UuidGenerator,
|
||||
VectorMemoryRecall, DEFAULT_OLLAMA_BASE_URL, ONNX_CACHE_SUBDIR, RECOMMENDED_ONNX_MODELS,
|
||||
VECTOR_HTTP_ENABLED, VECTOR_ONNX_ENABLED,
|
||||
FsIssueNumberAllocator, FsIssueStore, FsLiveStateStore, FsMemoryStore, FsOrchestratorWatcher,
|
||||
FsPermissionStore, FsProfileStore, FsProjectStore, FsProviderSessionStore, FsSkillStore,
|
||||
FsTemplateStore, Git2Repository, HeuristicHandoffSummarizer, IdeaiContextStore,
|
||||
InMemoryConversationRegistry, InMemoryMailbox, LocalFileSystem, LocalProcessSpawner, McpServer,
|
||||
MediatedInbox, NaiveMemoryRecall, OrchestratorWatchHandle, PortablePtyAdapter, RwFileGuard,
|
||||
StructuredSessionFactory, SystemClock, SystemMillisClock, TicketToolProvider,
|
||||
TokioBroadcastEventBus, TokioScheduler, UuidGenerator, VectorMemoryRecall,
|
||||
DEFAULT_OLLAMA_BASE_URL, ONNX_CACHE_SUBDIR, RECOMMENDED_ONNX_MODELS, VECTOR_HTTP_ENABLED,
|
||||
VECTOR_ONNX_ENABLED,
|
||||
};
|
||||
|
||||
use crate::chat::ChatBridge;
|
||||
use crate::mcp_endpoint::{mcp_endpoint, AppMcpRuntimeProvider, McpEndpoint};
|
||||
use crate::pty::PtyBridge;
|
||||
use crate::tickets::AppTicketToolProvider;
|
||||
|
||||
use infrastructure::StdioTransport;
|
||||
|
||||
@ -442,6 +445,26 @@ pub struct AppState {
|
||||
pub terminal_sessions: Arc<TerminalSessions>,
|
||||
/// The domain event bus (also handed to the event relay).
|
||||
pub event_bus: Arc<TokioBroadcastEventBus>,
|
||||
/// Create an issue-backed public ticket.
|
||||
pub create_issue: Arc<CreateIssue>,
|
||||
/// Read an issue-backed public ticket.
|
||||
pub read_issue: Arc<ReadIssue>,
|
||||
/// List issue-backed public tickets.
|
||||
pub list_issues: Arc<ListIssues>,
|
||||
/// Update an issue-backed public ticket.
|
||||
pub update_issue: Arc<UpdateIssue>,
|
||||
/// Read a ticket carnet.
|
||||
pub read_issue_carnet: Arc<ReadIssueCarnet>,
|
||||
/// Update a ticket carnet.
|
||||
pub update_issue_carnet: Arc<UpdateIssueCarnet>,
|
||||
/// Link two public tickets.
|
||||
pub link_issues: Arc<LinkIssues>,
|
||||
/// Unlink public tickets.
|
||||
pub unlink_issues: Arc<UnlinkIssues>,
|
||||
/// Assign or unassign an agent on a public ticket.
|
||||
pub assign_issue_agent: Arc<AssignIssueAgent>,
|
||||
/// MCP provider for the public `idea_ticket_*` tools.
|
||||
pub(crate) ticket_tool_provider: Arc<dyn TicketToolProvider>,
|
||||
/// Generic PTY↔Channel bridge registry (consumed by L3).
|
||||
pub pty_bridge: Arc<PtyBridge>,
|
||||
/// Registre des sessions structurées (IA / cellules chat, §17.5). Partagé avec
|
||||
@ -795,6 +818,62 @@ impl AppState {
|
||||
let contexts = Arc::new(IdeaiContextStore::new(Arc::clone(&fs_port)));
|
||||
let contexts_port = Arc::clone(&contexts) as Arc<dyn AgentContextStore>;
|
||||
|
||||
// --- Public tickets (Issue domain) ---
|
||||
// One stateless FS store/allocator serves every project; the project root is
|
||||
// passed on each port call and resolves to `<root>/.ideai/tickets/`.
|
||||
let issue_store = Arc::new(FsIssueStore::new());
|
||||
let issue_store_port = Arc::clone(&issue_store) as Arc<dyn IssueStore>;
|
||||
let issue_allocator = Arc::new(FsIssueNumberAllocator::new());
|
||||
let issue_allocator_port = Arc::clone(&issue_allocator) as Arc<dyn IssueNumberAllocator>;
|
||||
let create_issue = Arc::new(CreateIssue::new(
|
||||
Arc::clone(&issue_store_port),
|
||||
Arc::clone(&issue_allocator_port),
|
||||
Arc::clone(&contexts_port),
|
||||
Arc::clone(&ids) as Arc<dyn IdGenerator>,
|
||||
Arc::clone(&clock) as Arc<dyn Clock>,
|
||||
Arc::clone(&events_port),
|
||||
));
|
||||
let read_issue = Arc::new(ReadIssue::new(Arc::clone(&issue_store_port)));
|
||||
let list_issues = Arc::new(ListIssues::new(Arc::clone(&issue_store_port)));
|
||||
let update_issue = Arc::new(UpdateIssue::new(
|
||||
Arc::clone(&issue_store_port),
|
||||
Arc::clone(&contexts_port),
|
||||
Arc::clone(&clock) as Arc<dyn Clock>,
|
||||
Arc::clone(&events_port),
|
||||
));
|
||||
let read_issue_carnet = Arc::new(ReadIssueCarnet::new(Arc::clone(&issue_store_port)));
|
||||
let update_issue_carnet = Arc::new(UpdateIssueCarnet::new(
|
||||
Arc::clone(&issue_store_port),
|
||||
Arc::clone(&clock) as Arc<dyn Clock>,
|
||||
Arc::clone(&events_port),
|
||||
));
|
||||
let link_issues = Arc::new(LinkIssues::new(
|
||||
Arc::clone(&issue_store_port),
|
||||
Arc::clone(&clock) as Arc<dyn Clock>,
|
||||
Arc::clone(&events_port),
|
||||
));
|
||||
let unlink_issues = Arc::new(UnlinkIssues::new(
|
||||
Arc::clone(&issue_store_port),
|
||||
Arc::clone(&clock) as Arc<dyn Clock>,
|
||||
Arc::clone(&events_port),
|
||||
));
|
||||
let assign_issue_agent = Arc::new(AssignIssueAgent::new(
|
||||
Arc::clone(&issue_store_port),
|
||||
Arc::clone(&contexts_port),
|
||||
Arc::clone(&clock) as Arc<dyn Clock>,
|
||||
Arc::clone(&events_port),
|
||||
));
|
||||
let ticket_tool_provider: Arc<dyn TicketToolProvider> = Arc::new(AppTicketToolProvider {
|
||||
create: Arc::clone(&create_issue),
|
||||
read: Arc::clone(&read_issue),
|
||||
list: Arc::clone(&list_issues),
|
||||
update: Arc::clone(&update_issue),
|
||||
read_carnet: Arc::clone(&read_issue_carnet),
|
||||
update_carnet: Arc::clone(&update_issue_carnet),
|
||||
link: Arc::clone(&link_issues),
|
||||
unlink: Arc::clone(&unlink_issues),
|
||||
});
|
||||
|
||||
// --- Project permissions (LP1) ---
|
||||
let permission_store = Arc::new(FsPermissionStore::new(Arc::clone(&fs_port)));
|
||||
let permission_store_port = Arc::clone(&permission_store) as Arc<dyn PermissionStore>;
|
||||
@ -1500,6 +1579,16 @@ impl AppState {
|
||||
pty_port,
|
||||
terminal_sessions,
|
||||
event_bus,
|
||||
create_issue,
|
||||
read_issue,
|
||||
list_issues,
|
||||
update_issue,
|
||||
read_issue_carnet,
|
||||
update_issue_carnet,
|
||||
link_issues,
|
||||
unlink_issues,
|
||||
assign_issue_agent,
|
||||
ticket_tool_provider,
|
||||
pty_bridge,
|
||||
structured_sessions,
|
||||
chat_bridge,
|
||||
@ -1654,7 +1743,8 @@ impl AppState {
|
||||
let handle = McpServerHandle::start(
|
||||
McpServer::new(Arc::clone(&self.orchestrator_service), project.clone())
|
||||
.with_events(events)
|
||||
.with_ready_sink(ready_sink),
|
||||
.with_ready_sink(ready_sink)
|
||||
.with_ticket_tools(Arc::clone(&self.ticket_tool_provider)),
|
||||
endpoint,
|
||||
listener,
|
||||
project_id,
|
||||
@ -3343,6 +3433,7 @@ mod mcp_serve_peer_tests {
|
||||
let names: Vec<&str> = tools.iter().map(|t| t["name"].as_str().unwrap()).collect();
|
||||
for expected in [
|
||||
"idea_list_agents",
|
||||
"idea_ask_agent",
|
||||
"idea_launch_agent",
|
||||
"idea_stop_agent",
|
||||
"idea_update_context",
|
||||
@ -3357,18 +3448,28 @@ mod mcp_serve_peer_tests {
|
||||
// Live-state (programme live-state, lot LS4).
|
||||
"idea_workstate_read",
|
||||
"idea_workstate_set",
|
||||
// Public ticket tools (Issue domain).
|
||||
"idea_ticket_create",
|
||||
"idea_ticket_read",
|
||||
"idea_ticket_list",
|
||||
"idea_ticket_update",
|
||||
"idea_ticket_update_status",
|
||||
"idea_ticket_update_priority",
|
||||
"idea_ticket_read_carnet",
|
||||
"idea_ticket_update_carnet",
|
||||
"idea_ticket_link",
|
||||
"idea_ticket_unlink",
|
||||
] {
|
||||
assert!(
|
||||
names.contains(&expected),
|
||||
"missing tool {expected}; got {names:?}"
|
||||
);
|
||||
}
|
||||
assert!(!names.contains(&"idea_ask_agent"));
|
||||
assert!(!names.contains(&"idea_reply"));
|
||||
assert_eq!(
|
||||
tools.len(),
|
||||
12,
|
||||
"exactly the twelve non-conversation idea_* tools; got {names:?}"
|
||||
23,
|
||||
"exactly the twenty-three exposed idea_* tools; got {names:?}"
|
||||
);
|
||||
|
||||
drop(client); // EOF ⇒ serve loop ends
|
||||
|
||||
1025
crates/app-tauri/src/tickets.rs
Normal file
1025
crates/app-tauri/src/tickets.rs
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user