Spensara Spensara Knowledge Base & Documentation Repository

Repository Docs & Knowledge Base

Consolidated Spensara documentation repository (722 Markdown & architecture documents in ../repository)
Built Word Documents (.docx): Spensara Overview (.docx) Capability Assessment (.docx) Brink POS Ingestion Analysis (.docx) The Borg Ingestion Architecture (.docx)

Implementation Specs (157)

Detailed engineering and feature implementation specifications.
implementation-specs/specs/AnomalyDetection/TASK4.1.md 6,259 bytes · Updated Oct 23, 2026 10:44 AM EDT
Raw File

TASK 4.1: Create IMonitorRunOrchestrator Interface

Summary

Define the interface for the orchestrator that coordinates the complete anomaly detection execution flow: loading specs, running the agent, and emitting results.

Dependencies

  • TASK 1.2 (MonitorRun entity)
  • TASK 2.1 (AnomalyDetectionResult)

Files to Create

1. backend/Kds.Ai.AnomalyDetector/Orchestration/IMonitorRunOrchestrator.cs

csharp
using Kds.Ai.Data.Entities;

namespace Kds.Ai.AnomalyDetector.Orchestration;

/// <summary>
/// Orchestrates the execution of monitor runs, coordinating between
/// MonitorSpec loading, agent execution, and result emission
/// </summary>
public interface IMonitorRunOrchestrator
{
    /// <summary>
    /// Execute a monitor run by MonitorSpec ID
    /// </summary>
    /// <param name="monitorSpecId">The ID of the MonitorSpec to run</param>
    /// <param name="analysisTime">Point in time to analyze (default: now)</param>
    /// <param name="options">Execution options</param>
    /// <param name="ct">Cancellation token</param>
    /// <returns>The completed MonitorRun record</returns>
    Task<MonitorRun> ExecuteAsync(
        Guid monitorSpecId,
        DateTimeOffset? analysisTime = null,
        RunOptions? options = null,
        CancellationToken ct = default);
    
    /// <summary>
    /// Execute a monitor run with a provided MonitorSpec
    /// (useful for testing or one-off runs without database storage)
    /// </summary>
    /// <param name="spec">The MonitorSpec to execute</param>
    /// <param name="analysisTime">Point in time to analyze (default: now)</param>
    /// <param name="options">Execution options</param>
    /// <param name="ct">Cancellation token</param>
    /// <returns>The completed MonitorRun record</returns>
    Task<MonitorRun> ExecuteAsync(
        MonitorSpec spec,
        DateTimeOffset? analysisTime = null,
        RunOptions? options = null,
        CancellationToken ct = default);
    
    /// <summary>
    /// Execute all active monitors (State == Active)
    /// </summary>
    /// <param name="analysisTime">Point in time to analyze</param>
    /// <param name="options">Execution options</param>
    /// <param name="ct">Cancellation token</param>
    /// <returns>List of completed MonitorRun records</returns>
    Task<IReadOnlyList<MonitorRun>> ExecuteAllActiveAsync(
        DateTimeOffset? analysisTime = null,
        RunOptions? options = null,
        CancellationToken ct = default);
}

/// <summary>
/// Options for controlling monitor execution
/// </summary>
public record RunOptions
{
    /// <summary>
    /// If true, run the analysis but don't emit alerts or save to database
    /// </summary>
    public bool DryRun { get; init; } = false;
    
    /// <summary>
    /// If true, suppress alert emission even if anomaly detected
    /// </summary>
    public bool SuppressAlerts { get; init; } = false;
    
    /// <summary>
    /// Override the analysis window start time
    /// </summary>
    public DateTimeOffset? WindowStart { get; init; }
    
    /// <summary>
    /// Override the analysis window end time
    /// </summary>
    public DateTimeOffset? WindowEnd { get; init; }
    
    /// <summary>
    /// Maximum execution time before timeout
    /// </summary>
    public TimeSpan? Timeout { get; init; }
    
    /// <summary>
    /// Callback for progress reporting
    /// </summary>
    public IProgress<RunProgress>? Progress { get; init; }
    
    /// <summary>
    /// Default options
    /// </summary>
    public static RunOptions Default => new();
    
    /// <summary>
    /// Options for dry-run testing
    /// </summary>
    public static RunOptions TestRun => new()
    {
        DryRun = true,
        SuppressAlerts = true
    };
}

/// <summary>
/// Progress information during monitor execution
/// </summary>
public record RunProgress
{
    /// <summary>
    /// Current phase of execution
    /// </summary>
    public required RunPhase Phase { get; init; }
    
    /// <summary>
    /// Human-readable message
    /// </summary>
    public required string Message { get; init; }
    
    /// <summary>
    /// Progress percentage (0-100) if applicable
    /// </summary>
    public int? ProgressPercent { get; init; }
}

/// <summary>
/// Phases of monitor execution
/// </summary>
public enum RunPhase
{
    Initializing,
    LoadingSpec,
    RunningAgent,
    AnalyzingResults,
    EmittingAlerts,
    SavingResults,
    Completed,
    Failed
}

Interface Design Rationale

Multiple Entry Points

  1. By ID: Standard case - load spec from DB and run
  2. By Spec: Testing/ad-hoc without DB
  3. All Active: Scheduled batch runs (monitors with State == Active)

RunOptions

Flexible options enable:

  • DryRun: Test without side effects
  • SuppressAlerts: Run analysis without notification
  • Window Override: Custom time ranges for historical analysis
  • Timeout: Prevent runaway executions
  • Progress: UI/logging integration

Removed from Old Design

  • ExecuteAllByTypeAsync(monitorType) - removed because MonitorSpec no longer has MonitorType
  • ExecuteForStoreAsync(storeGuid) - removed because MonitorSpec no longer has StoreGuid
  • SkipPreflight option - removed because preflight checks were domain-specific (data freshness); the agent can decide in its own reasoning whether data is fresh enough

Progress Reporting

The IProgress<RunProgress> pattern allows:

  • CLI progress indicators
  • UI status updates
  • Logging integration
  • Debugging long-running operations

Acceptance Criteria

  • [ ] IMonitorRunOrchestrator interface created
  • [ ] All execution methods defined
  • [ ] RunOptions record with all options
  • [ ] RunProgress and RunPhase for progress tracking
  • [ ] No references to MonitorType, StoreGuid, or IsEnabled
  • [ ] Uses ExecuteAllActiveAsync (not by type/store)
  • [ ] Static factory methods for common option sets
  • [ ] XML documentation complete
  • [ ] Solution builds without errors

Notes

  • Implementation in TASK 4.2
  • Consider adding CancelAsync(Guid runId) for aborting runs
  • May want GetRunStatusAsync(Guid runId) for polling
  • The return type is MonitorRun entity - consider a DTO if needed