Built Word Documents (.docx):
Spensara Overview (.docx)
Capability Assessment (.docx)
Brink POS Ingestion Analysis (.docx)
The Borg Ingestion Architecture (.docx)
Categories
Implementation Specs (157)
Detailed engineering and feature implementation specifications.
- specs/AnomalyDetection/TASK1.1.md
- specs/AnomalyDetection/TASK1.2.md
- specs/AnomalyDetection/TASK1.3.md
- specs/AnomalyDetection/TASK1.4.md
- specs/AnomalyDetection/TASK2.1.md
- specs/AnomalyDetection/TASK2.2.md
- specs/AnomalyDetection/TASK3.1.md
- specs/AnomalyDetection/TASK3.2.md
- specs/AnomalyDetection/TASK3.3.md
- specs/AnomalyDetection/TASK4.1.md
- specs/AnomalyDetection/TASK4.2.md
- specs/AnomalyDetection/TASK4.3.md
- specs/AnomalyDetection/TASK4.4.md
- specs/AnomalyDetection/TASK5.1.md
- specs/AnomalyDetection/TASK5.2.md
- specs/AnomalyDetection/TASK5.3.md
- specs/AnomalyDetection/TASK6.1.md
- specs/AnomalyDetection/TASK6.2.md
- specs/AnomalyDetection/TASK6.3.md
- specs/AnomalyDetection/TASKS.md
- specs/ApiServicePostgresTestStability/TASK.md
- specs/BackendDatabaseConsolidation/TASK.md
- specs/BackendDatabaseConsolidation/TASK1.1.md
- specs/BackendDatabaseConsolidation/TASK1.2.md
- specs/BackendDatabaseConsolidation/TASK1.3.md
- specs/BackendDatabaseConsolidation/TASK2.1.md
- specs/BackendDatabaseConsolidation/TASK2.2.md
- specs/BackendDatabaseConsolidation/TASK2.3.md
- specs/BackendDatabaseConsolidation/TASK3.1.md
- specs/BackendDatabaseConsolidation/TASK3.2.md
- specs/BackendDatabaseConsolidation/TASK4.1.md
- specs/BackendDatabaseConsolidation/TASK4.2.md
- specs/BackendDatabaseConsolidation/TASK4.3.md
- specs/BackendDatabaseConsolidation/TASK5.1.md
- specs/BackendDatabaseConsolidation/TASK5.2.md
- specs/BrinkLaborGetShiftsStaging/TASK.md
- specs/BrinkPosOrdersFirstEtlJob/TASK.md
- specs/BrinkPosOrdersFirstEtlJob/TASK0.md
- specs/BrinkPosOrdersFirstEtlJob/TASK1.md
- specs/BrinkPosOrdersFirstEtlJob/TASK2.1.md
- specs/BrinkPosOrdersFirstEtlJob/TASK2.2.md
- specs/BrinkPosOrdersFirstEtlJob/TASK2.3.md
- specs/BrinkPosOrdersFirstEtlJob/TASK2.4.md
- specs/BrinkPosOrdersFirstEtlJob/TASK2.md
- specs/BrinkPosOrdersFirstEtlJob/TASK3.1.md
- specs/BrinkPosOrdersFirstEtlJob/TASK3.2.md
- specs/BrinkPosOrdersFirstEtlJob/TASK3.3.md
- specs/BrinkPosOrdersFirstEtlJob/TASK3.4.md
- specs/BrinkPosOrdersFirstEtlJob/TASK3.md
- specs/BrinkPosOrdersFirstEtlJob/TASK4.1.md
- specs/BrinkPosOrdersFirstEtlJob/TASK4.2.md
- specs/BrinkPosOrdersFirstEtlJob/TASK4.3.md
- specs/BrinkPosOrdersFirstEtlJob/TASK4.4.md
- specs/BrinkPosOrdersFirstEtlJob/TASK4.md
- specs/BrinkPosOrdersFirstEtlJob/TASK5.1.md
- specs/BrinkPosOrdersFirstEtlJob/TASK5.2.md
- specs/BrinkPosOrdersFirstEtlJob/TASK5.3.md
- specs/BrinkPosOrdersFirstEtlJob/TASK5.4.md
- specs/BrinkPosOrdersFirstEtlJob/TASK5.md
- specs/BrinkPosOrdersFirstEtlJob/TASK6.1.md
- specs/BrinkPosOrdersFirstEtlJob/TASK6.2.md
- specs/BrinkPosOrdersFirstEtlJob/TASK6.3.md
- specs/BrinkPosOrdersFirstEtlJob/TASK6.4.md
- specs/BrinkPosOrdersFirstEtlJob/TASK6.md
- specs/BrinkPosOrdersFirstEtlJob/TASK7.md
- specs/BrinkPosOrdersFirstEtlJob/TASK8.md
- specs/BrinkPosOrdersFirstEtlJob/TASK9.md
- specs/BrinkSettings2ToStaging/TASK.MD
- specs/CuratedToS3Stage/TASK.md
- specs/CuratedToS3Stage/TASK0.md
- specs/CuratedToS3Stage/TASK1.md
- specs/CuratedToS3Stage/TASK2.md
- specs/CuratedToS3Stage/TASK3.md
- specs/CuratedToS3Stage/TASK4.md
- specs/CustomerCredentialCustomHeaders/TASK.md
- specs/CustomerCredentialCustomHeaders/TASK1.1.md
- specs/CustomerCredentialCustomHeaders/TASK1.2.md
- specs/CustomerCredentialCustomHeaders/TASK2.1.md
- specs/CustomerCredentialCustomHeaders/TASK2.2.md
- specs/CustomerCredentialCustomHeaders/TASK3.1.md
- specs/CustomerCredentialTriggeredProvisioning/TASK.md
- specs/CustomerCredentialTriggeredProvisioning/TASK1.1.md
- specs/CustomerCredentialTriggeredProvisioning/TASK1.2.md
- specs/CustomerCredentialTriggeredProvisioning/TASK2.1.md
- specs/CustomerCredentialTriggeredProvisioning/TASK2.2.md
- specs/CustomerCredentialTriggeredProvisioning/TASK3.1.md
- specs/CustomerCredentialTriggeredProvisioning/TASK3.2.md
- specs/CustomerCredentialTriggeredProvisioning/TASK4.1.md
- specs/EtlSourceAdapterFramework/TASK.md
- specs/EtlSourceAdapterFramework/TASK0.md
- specs/EtlSourceAdapterFramework/TASK1.md
- specs/EtlSourceAdapterFramework/TASK2.md
- specs/EtlSourceAdapterFramework/TASK3.md
- specs/EtlSourceAdapterFramework/TASK4.md
- specs/EtlSourceAdapterFramework/TASK5.md
- specs/EtlSourceAdapterFramework/TASK6.md
- specs/KdsCdcDebeziumIntegration/TASK.md
- specs/KdsCdcDebeziumIntegration/TASK1.md
- specs/KdsCdcDebeziumIntegration/TASK2.md
- specs/KdsCdcDebeziumIntegration/TASK3.md
- specs/KdsCdcDebeziumIntegration/TASK4.md
- specs/KdsCdcDebeziumIntegration/TASK5.md
- specs/KdsCdcDurableRelationRouting/TASK.md
- specs/KdsCdcDurableRelationRouting/TASK1.md
- specs/KdsCdcDurableRelationRouting/TASK2.md
- specs/KdsCdcDurableRelationRouting/TASK3.md
- specs/KdsCdcDurableRelationRouting/TASK4.md
- specs/KdsCdcDurableRelationRouting/TASK5.md
- specs/KdsCdcIngestion/TASK.md
- specs/KdsCdcIngestion/TASK1.md
- specs/KdsCdcIngestion/TASK2.md
- specs/KdsCdcIngestion/TASK3.md
- specs/KdsCdcIngestion/TASK4.md
- specs/KdsCdcIngestion/TASK5.md
- specs/KdsCdcIngestion/TASK6.md
- specs/KdsCdcIngestion/TASK7.md
- specs/KdsCdcIngestion/TASK8.md
- specs/KdsCdcIngestion/TASK9.md
- specs/KdsCdcReplayExecution/TASK.md
- specs/KdsCdcReplayExecution/TASK1.md
- specs/KdsCdcReplayExecution/TASK2.md
- specs/KdsCdcReplayExecution/TASK3.md
- specs/KdsCdcReplayExecution/TASK4.md
- specs/KdsMonitorServingContractMigration/TASK.md
- specs/KdsMonitorServingContractMigration/TASK1.1.md
- specs/KdsMonitorServingContractMigration/TASK1.2.md
- specs/KdsMonitorServingContractMigration/TASK2.1.md
- specs/KdsMonitorServingContractMigration/TASK2.2.md
- specs/KdsMonitorServingContractMigration/TASK3.1.md
- specs/KdsMonitorServingContractMigration/TASK3.2.md
- specs/RedshiftFoundation/TASK.md
- specs/RedshiftFoundation/TASK1.1.md
- specs/RedshiftFoundation/TASK1.2.md
- specs/RedshiftFoundation/TASK1.3.md
- specs/RedshiftFoundation/TASK1.4.md
- specs/RedshiftFoundation/TASK2.1.md
- specs/RedshiftFoundation/TASK2.2.md
- specs/RedshiftFoundation/TASK3.1.md
- specs/RedshiftFoundation/TASK3.2.md
- specs/RedshiftFoundation/TASK3.3.md
- specs/RedshiftSyntheticDataFeed/TASK.md
- specs/RedshiftSyntheticDataFeed/TASK1.1.md
- specs/RedshiftSyntheticDataFeed/TASK1.2.md
- specs/RedshiftSyntheticDataFeed/TASK1.3.md
- specs/RedshiftSyntheticDataFeed/TASK2.1.md
- specs/RedshiftSyntheticDataFeed/TASK2.2.md
- specs/RedshiftSyntheticDataFeed/TASK3.1.md
- specs/RedshiftSyntheticDataFeed/TASK3.2.md
- specs/SingleStoreRedshiftRuntimeReadPath/TASK.md
- specs/SingleStoreRedshiftRuntimeReadPath/TASK1.1.md
- specs/SingleStoreRedshiftRuntimeReadPath/TASK1.2.md
- specs/SingleStoreRedshiftRuntimeReadPath/TASK2.1.md
- specs/SingleStoreRedshiftRuntimeReadPath/TASK2.2.md
- specs/SingleStoreRedshiftRuntimeReadPath/TASK2.3.md
- specs/SingleStoreRedshiftRuntimeReadPath/TASK3.1.md
- specs/SingleStoreRedshiftRuntimeReadPath/TASK3.2.md
- specs/SingleStoreRedshiftRuntimeReadPath/TASK4.1.md
TASK 4.3: Add AlertEvent Emission Logic on Anomaly Detection
Summary
Create the alert emitter service that converts anomaly detection results into AlertEvent entities and persists them to the database.
Dependencies
- TASK 1.1 (MonitorSpec for context)
- TASK 2.1 (AnomalyDetectionResult as input)
- Existing
AlertEvententity from Kds.Ai.Data
Files to Create
1. backend/Kds.Ai.AnomalyDetector/Alerts/IAlertEmitter.cs
csharp
using Kds.Ai.AnomalyDetector.Agents;
using Kds.Ai.Data.Entities;
namespace Kds.Ai.AnomalyDetector.Alerts;
/// <summary>
/// Service for emitting alerts based on anomaly detection results
/// </summary>
public interface IAlertEmitter
{
/// <summary>
/// Emit an alert for a detected anomaly
/// </summary>
/// <param name="spec">The monitor spec that detected the anomaly</param>
/// <param name="result">The detection result</param>
/// <param name="ct">Cancellation token</param>
/// <returns>The created alert event (Id == Guid.Empty if suppressed)</returns>
Task<AlertEvent> EmitAsync(
MonitorSpec spec,
AnomalyDetectionResult result,
CancellationToken ct = default);
/// <summary>
/// Check if an alert should be suppressed due to deduplication
/// </summary>
/// <param name="spec">The monitor spec</param>
/// <param name="result">The detection result</param>
/// <param name="ct">Cancellation token</param>
/// <returns>True if alert should be suppressed</returns>
Task<bool> ShouldSuppressAsync(
MonitorSpec spec,
AnomalyDetectionResult result,
CancellationToken ct = default);
}2. backend/Kds.Ai.AnomalyDetector/Alerts/AlertEmitter.cs
csharp
using System.Security.Cryptography;
using System.Text;
using Kds.Ai.AnomalyDetector.Agents;
using Kds.Ai.Data;
using Kds.Ai.Data.Entities;
using Kds.Ai.Data.Enums;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace Kds.Ai.AnomalyDetector.Alerts;
internal sealed class AlertEmitter(
KdsAiDbContext dbContext,
ILogger<AlertEmitter> logger) : IAlertEmitter
{
// Deduplication window - suppress similar alerts within this period
private static readonly TimeSpan DedupeWindow = TimeSpan.FromHours(4);
public async Task<AlertEvent> EmitAsync(
MonitorSpec spec,
AnomalyDetectionResult result,
CancellationToken ct = default)
{
if (!result.AnomalyDetected)
{
throw new InvalidOperationException("Cannot emit alert for non-anomaly result");
}
// Check deduplication
if (await ShouldSuppressAsync(spec, result, ct))
{
logger.LogInformation(
"Suppressing duplicate alert for monitor '{MonitorName}'",
spec.Name);
// Return a "suppressed" marker alert (not saved)
return new AlertEvent
{
Id = Guid.Empty, // Indicates not saved
Type = EventType.AiAnomaly,
Severity = result.Severity ?? EventSeverity.Low,
Timestamp = DateTimeOffset.UtcNow,
Name = $"[SUPPRESSED] {BuildAlertName(spec, result)}",
Summary = "Alert suppressed due to deduplication",
Content = result.DetailedAnalysis ?? ""
};
}
var alert = new AlertEvent
{
Id = Guid.NewGuid(),
Type = EventType.AiAnomaly,
Severity = result.Severity ?? EventSeverity.Low,
Timestamp = DateTimeOffset.UtcNow,
Name = BuildAlertName(spec, result),
Summary = BuildAlertSummary(spec, result),
Content = BuildAlertContent(spec, result)
};
dbContext.Events.Add(alert);
await dbContext.SaveChangesAsync(ct);
logger.LogInformation(
"Emitted {Severity} alert {AlertId}: {Name}",
alert.Severity, alert.Id, alert.Name);
return alert;
}
public async Task<bool> ShouldSuppressAsync(
MonitorSpec spec,
AnomalyDetectionResult result,
CancellationToken ct = default)
{
// Check for recent similar alerts from the same monitor
var cutoff = DateTimeOffset.UtcNow - DedupeWindow;
var alertPrefix = $"[{spec.Name}]";
var recentSimilar = await dbContext.Events
.Where(e => e.Type == EventType.AiAnomaly)
.Where(e => e.Timestamp > cutoff)
.Where(e => e.Name.StartsWith(alertPrefix))
.AnyAsync(ct);
return recentSimilar;
}
private static string BuildAlertName(MonitorSpec spec, AnomalyDetectionResult result)
{
// Format: [MonitorName] - Summary
var summaryPart = result.Summary?.Length > 60
? result.Summary[..60] + "..."
: result.Summary ?? "Anomaly Detected";
return $"[{spec.Name}] {summaryPart}";
}
private static string BuildAlertSummary(MonitorSpec spec, AnomalyDetectionResult result)
{
if (!string.IsNullOrEmpty(result.Summary))
{
return result.Summary;
}
var findingCount = result.Findings.Count;
return findingCount switch
{
0 => "Anomaly detected in monitoring analysis",
1 => result.Findings[0].Description,
_ => $"{findingCount} anomalies detected by {spec.Name}"
};
}
private static string BuildAlertContent(MonitorSpec spec, AnomalyDetectionResult result)
{
var sb = new StringBuilder();
// Header
sb.AppendLine($"# {spec.Name} - Alert");
sb.AppendLine();
sb.AppendLine($"**Monitor:** {spec.Name}");
sb.AppendLine($"**Severity:** {result.Severity}");
sb.AppendLine($"**Generated:** {DateTimeOffset.UtcNow:yyyy-MM-dd HH:mm:ss} UTC");
sb.AppendLine();
// Summary
if (!string.IsNullOrEmpty(result.Summary))
{
sb.AppendLine("## Summary");
sb.AppendLine();
sb.AppendLine(result.Summary);
sb.AppendLine();
}
// Findings
if (result.Findings.Count > 0)
{
sb.AppendLine("## Findings");
sb.AppendLine();
foreach (var finding in result.Findings)
{
sb.AppendLine($"### {finding.Description}");
sb.AppendLine();
sb.AppendLine($"- **Severity:** {finding.Severity}");
if (finding.AffectedPeriod is not null)
sb.AppendLine($"- **Period:** {finding.AffectedPeriod}");
if (finding.Evidence is not null)
sb.AppendLine($"- **Evidence:** {finding.Evidence}");
sb.AppendLine();
}
}
// Detailed Analysis
if (!string.IsNullOrEmpty(result.DetailedAnalysis))
{
sb.AppendLine("## Detailed Analysis");
sb.AppendLine();
sb.AppendLine(result.DetailedAnalysis);
sb.AppendLine();
}
// Warnings
if (result.Warnings.Count > 0)
{
sb.AppendLine("## Warnings");
sb.AppendLine();
foreach (var warning in result.Warnings)
{
sb.AppendLine($"- {warning}");
}
sb.AppendLine();
}
return sb.ToString();
}
private static string GenerateFingerprint(MonitorSpec spec, AnomalyDetectionResult result)
{
// Create a fingerprint based on monitor name and severity
var input = $"{spec.Name}|{result.Severity}|" +
string.Join(",", result.Findings.Select(f => f.Description).Distinct());
var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(input));
return Convert.ToHexString(bytes)[..16];
}
}Files to Modify
3. backend/Kds.Ai.AnomalyDetector/DependencyInjectionExtensions.cs
Add emitter registration:
csharp
// Register alert emitter
builder.Services.AddScoped<IAlertEmitter, AlertEmitter>();Alert Content Structure
The generated alert content follows this Markdown structure:
markdown
# Prep Time Monitor - Downtown - Alert
**Monitor:** Prep Time Monitor - Downtown
**Severity:** High
**Generated:** 2025-01-15 14:30:00 UTC
## Summary
Prep times 45% above baseline during lunch rush (11:00-14:00)
## Findings
### Significant increase in average prep time during peak hours
- **Severity:** High
- **Period:** 2025-01-15 12:00-13:00
- **Evidence:** Average 450s vs baseline 310s (45% increase, 2.8 standard deviations above mean)
## Detailed Analysis
[LLM-generated detailed analysis...]
## Warnings
- Sample size for 13:00 hour was below minimum thresholdDeduplication Logic
The deduplication prevents alert fatigue by:
- Checking for recent alerts with the same monitor name prefix within the dedupe window (4 hours)
- Suppressing if a similar alert exists
- The fingerprint method is available for future enhanced similarity matching
Changes from Old Design
- Dedup uses
spec.Nameprefix instead ofspec.MonitorType+spec.StoreGuid[..8] BuildAlertNameuses[MonitorName] Summaryformat instead of[MonitorType] Store: xxx - SummaryBuildAlertContentusesspec.Nameinstead ofspec.StoreGuidandspec.MonitorType- Findings use
Description,Severity,AffectedPeriod,Evidenceinstead ofType,DeviationPercent,ZScore,Confidence - Class is
internal sealedwith primary constructor
Acceptance Criteria
- [ ]
IAlertEmitterinterface created - [ ]
AlertEmitterimplementation created (internal sealedwith primary constructor) - [ ] Alert name format:
[MonitorName] Summary - [ ] Alert content in Markdown format
- [ ] Deduplication logic uses
spec.Nameprefix - [ ] Suppressed alerts return marker with
Id = Guid.Empty - [ ] No references to
MonitorType,StoreGuid,Type(on findings),DeviationPercent,ZScore,Confidence - [ ] Logging integrated
- [ ] Emitter registered in DI
- [ ] Solution builds without errors
Notes
- The dedupe window (4 hours) can be made configurable
- Consider adding alert channels (email, Slack) in future
- May want to add alert acknowledgment/resolution tracking
- Fingerprinting could be more sophisticated (e.g., LSH for similarity)