Initial scaffold: GyanDoot v2.0 in Rust

Full Cargo workspace with 11 crates:
- gyandoot-core: Shared types, config, error types
- gyandoot-agent: Agent loop, tool execution, context compression
- gyandoot-providers: LLM provider abstraction (OpenAI, Anthropic, Ollama)
- gyandoot-memory: MemPalace persistent memory system
- gyandoot-platform: Platform adapters (Telegram, Discord, Webhook, CLI)
- gyandoot-security: Credential vault, sanitizer, threat detector
- gyandoot-kanban: Task decomposition, swarm orchestration, diagnostics
- gyandoot-subagents: Sub-agent spawning and orchestration
- gyandoot-skills: Skills discovery, loading, preprocessing, bundles
- gyandoot-profile: Multi-agent profile manager
- gyandoot-gateway: Gateway sessions, routing, access control

Plus: CLI entry point with clap, cross-platform installer script,
KANBAN.md, .gnpl/AGENTS.md

Workspace compiles clean: cargo check passes with 0 errors.
This commit is contained in:
2026-06-11 20:04:12 +05:30
commit 851c838ca5
53 changed files with 8037 additions and 0 deletions

View File

@@ -0,0 +1,152 @@
//! Diagnostics — task tracking, bottlenecks, and performance metrics.
use std::collections::HashMap;
use chrono::{DateTime, Utc};
use gyandoot_core::{KanbanResult, Task, TaskStatus};
use uuid::Uuid;
/// A diagnostic snapshot of the Kanban system.
#[derive(Debug, Clone)]
pub struct KanbanDiagnostics {
pub total_tasks: usize,
pub backlog_count: usize,
pub in_progress_count: usize,
pub done_count: usize,
pub blocked_count: usize,
pub average_completion_time_ms: Option<f64>,
pub bottlenecks: Vec<String>,
pub timestamp: DateTime<Utc>,
}
/// Tracks task lifecycle and produces diagnostic information.
pub struct Diagnostics {
task_timings: HashMap<Uuid, TaskTiming>,
bottlenecks: Vec<String>,
}
#[derive(Debug, Clone)]
struct TaskTiming {
created_at: DateTime<Utc>,
started_at: Option<DateTime<Utc>>,
completed_at: Option<DateTime<Utc>>,
status_changes: Vec<(DateTime<Utc>, TaskStatus)>,
}
impl Diagnostics {
/// Create a new diagnostics tracker.
pub fn new() -> Self {
Self {
task_timings: HashMap::new(),
bottlenecks: Vec::new(),
}
}
/// Record a task creation.
pub fn record_creation(&mut self, task_id: Uuid) {
self.task_timings.insert(
task_id,
TaskTiming {
created_at: Utc::now(),
started_at: None,
completed_at: None,
status_changes: vec![(Utc::now(), TaskStatus::Backlog)],
},
);
}
/// Record a task status change.
pub fn record_status_change(&mut self, task_id: &Uuid, new_status: TaskStatus) {
if let Some(timing) = self.task_timings.get_mut(task_id) {
timing.status_changes.push((Utc::now(), new_status.clone()));
if new_status == TaskStatus::InProgress && timing.started_at.is_none() {
timing.started_at = Some(Utc::now());
}
if new_status == TaskStatus::Done {
timing.completed_at = Some(Utc::now());
}
}
}
/// Generate a diagnostics snapshot for a set of tasks.
pub fn analyze(&self, tasks: &[Task]) -> KanbanResult<KanbanDiagnostics> {
let total = tasks.len();
let backlog = tasks.iter().filter(|t| t.status == TaskStatus::Backlog).count();
let in_progress = tasks.iter().filter(|t| t.status == TaskStatus::InProgress).count();
let done = tasks.iter().filter(|t| t.status == TaskStatus::Done).count();
let blocked = tasks.iter().filter(|t| t.status == TaskStatus::Blocked).count();
// Calculate average completion time
let completion_times: Vec<f64> = self
.task_timings
.values()
.filter_map(|t| {
let start = t.started_at?;
let end = t.completed_at?;
Some((end - start).num_milliseconds() as f64)
})
.collect();
let avg_completion = if completion_times.is_empty() {
None
} else {
Some(completion_times.iter().sum::<f64>() / completion_times.len() as f64)
};
Ok(KanbanDiagnostics {
total_tasks: total,
backlog_count: backlog,
in_progress_count: in_progress,
done_count: done,
blocked_count: blocked,
average_completion_time_ms: avg_completion,
bottlenecks: self.bottlenecks.clone(),
timestamp: Utc::now(),
})
}
/// Add a bottleneck warning.
pub fn report_bottleneck(&mut self, description: String) {
self.bottlenecks.push(description);
}
}
impl Default for Diagnostics {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_diagnostics_tracking() {
let mut diagnostics = Diagnostics::new();
let task_id = Uuid::new_v4();
diagnostics.record_creation(task_id);
diagnostics.record_status_change(&task_id, TaskStatus::InProgress);
diagnostics.record_status_change(&task_id, TaskStatus::Done);
let tasks = vec![Task {
id: task_id,
title: "Test".to_string(),
description: String::new(),
priority: gyandoot_core::Priority::Low,
status: TaskStatus::Done,
assigned_agent: None,
created_at: Utc::now(),
updated_at: Utc::now(),
subtasks: Vec::new(),
dependencies: Vec::new(),
tags: Vec::new(),
}];
let report = diagnostics.analyze(&tasks).unwrap();
assert_eq!(report.total_tasks, 1);
assert_eq!(report.done_count, 1);
}
}