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 3.2: Implement EfMonitorSpecRepository
Summary
Implement the IMonitorSpecRepository interface using Entity Framework Core for database access. The implementation lives in the Data project alongside the DbContext and entities it operates on.
Dependencies
- TASK 1.1 (MonitorSpec entity)
- TASK 1.3 (Migration applied)
- TASK 3.1 (IMonitorSpecRepository interface)
Files to Create
1. backend/Kds.Ai.Data/Repositories/EfMonitorSpecRepository.cs
csharp
using Kds.Ai.Data.Entities;
using Kds.Ai.Data.Enums;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace Kds.Ai.Data.Repositories;
/// <summary>
/// Entity Framework Core implementation of IMonitorSpecRepository
/// </summary>
public sealed class EfMonitorSpecRepository : IMonitorSpecRepository
{
private readonly KdsAiDbContext _dbContext;
private readonly ILogger<EfMonitorSpecRepository> _logger;
public EfMonitorSpecRepository(
KdsAiDbContext dbContext,
ILogger<EfMonitorSpecRepository> logger)
{
_dbContext = dbContext;
_logger = logger;
}
public async Task<MonitorSpec?> GetByIdAsync(Guid id, CancellationToken ct = default)
{
return await _dbContext.MonitorSpecs
.FirstOrDefaultAsync(x => x.Id == id, ct);
}
public async Task<MonitorSpec?> GetByNameAsync(string name, CancellationToken ct = default)
{
return await _dbContext.MonitorSpecs
.FirstOrDefaultAsync(x => x.Name == name, ct);
}
public async Task<IReadOnlyList<MonitorSpec>> GetByStateAsync(
MonitorState state,
CancellationToken ct = default)
{
return await _dbContext.MonitorSpecs
.Where(x => x.State == state)
.OrderBy(x => x.Name)
.ToListAsync(ct);
}
public async Task<IReadOnlyList<MonitorSpec>> GetActiveAsync(CancellationToken ct = default)
{
return await GetByStateAsync(MonitorState.Active, ct);
}
public async Task<IReadOnlyList<MonitorSpec>> GetAllAsync(CancellationToken ct = default)
{
return await _dbContext.MonitorSpecs
.OrderBy(x => x.Name)
.ToListAsync(ct);
}
public async Task<MonitorSpec> CreateAsync(MonitorSpec spec, CancellationToken ct = default)
{
if (spec.Id == Guid.Empty)
{
spec.Id = Guid.NewGuid();
}
spec.CreatedAt = DateTimeOffset.UtcNow;
_dbContext.MonitorSpecs.Add(spec);
await _dbContext.SaveChangesAsync(ct);
_logger.LogInformation(
"Created MonitorSpec {Id} '{Name}' in state {State}",
spec.Id, spec.Name, spec.State);
return spec;
}
public async Task<MonitorSpec> UpdateAsync(MonitorSpec spec, CancellationToken ct = default)
{
spec.UpdatedAt = DateTimeOffset.UtcNow;
_dbContext.MonitorSpecs.Update(spec);
await _dbContext.SaveChangesAsync(ct);
_logger.LogInformation("Updated MonitorSpec {Id}", spec.Id);
return spec;
}
public async Task UpdateLastRunAsync(Guid id, DateTimeOffset lastRunAt, CancellationToken ct = default)
{
var affected = await _dbContext.MonitorSpecs
.Where(x => x.Id == id)
.ExecuteUpdateAsync(setters => setters
.SetProperty(x => x.LastRunAt, lastRunAt)
.SetProperty(x => x.UpdatedAt, DateTimeOffset.UtcNow), ct);
if (affected == 0)
{
_logger.LogWarning("MonitorSpec {Id} not found for LastRunAt update", id);
}
}
public async Task SetStateAsync(Guid id, MonitorState state, CancellationToken ct = default)
{
var affected = await _dbContext.MonitorSpecs
.Where(x => x.Id == id)
.ExecuteUpdateAsync(setters => setters
.SetProperty(x => x.State, state)
.SetProperty(x => x.UpdatedAt, DateTimeOffset.UtcNow), ct);
if (affected == 0)
{
_logger.LogWarning("MonitorSpec {Id} not found for state transition", id);
}
else
{
_logger.LogInformation("MonitorSpec {Id} transitioned to {State}", id, state);
}
}
public async Task<bool> DeleteAsync(Guid id, CancellationToken ct = default)
{
var affected = await _dbContext.MonitorSpecs
.Where(x => x.Id == id)
.ExecuteDeleteAsync(ct);
if (affected > 0)
{
_logger.LogInformation("Deleted MonitorSpec {Id}", id);
return true;
}
_logger.LogWarning("MonitorSpec {Id} not found for deletion", id);
return false;
}
public async Task<bool> ExistsByNameAsync(string name, CancellationToken ct = default)
{
return await _dbContext.MonitorSpecs
.AnyAsync(x => x.Name == name, ct);
}
}Files to Modify
2. backend/Kds.Ai.Data/DependencyInjectionExtensions.cs
Add repository registration to the existing AddDataModule method:
csharp
using Kds.Ai.Data.Repositories;
// Inside AddDataModule, after the AddDbContext call:
// Register repositories
builder.Services.AddScoped<IMonitorSpecRepository, EfMonitorSpecRepository>();The full method should look like:
csharp
public static IHostApplicationBuilder AddDataModule(this IHostApplicationBuilder builder, string connectionName = "kdsaidb")
{
builder.Services.AddDbContext<KdsAiDbContext>(options =>
{
var connectionString = builder.Configuration.GetConnectionString(connectionName) ?? "Server=localhost;Database=dummy;";
options.UseMySQL(connectionString);
});
// Register repositories
builder.Services.AddScoped<IMonitorSpecRepository, EfMonitorSpecRepository>();
return builder;
}Implementation Details
Why Kds.Ai.Data?
The EF Core repository implementation belongs in the Data project because:
- It directly depends on
KdsAiDbContextdefined in the same project - It uses only types from the Data project (entities, enums)
- No external dependencies beyond what the Data project already has (
Microsoft.EntityFrameworkCore,Microsoft.Extensions.Logging) - DI registration in
AddDataModuleensures the repository is available to any project that callsAddDataModule, without requiring them to know about the implementation details - Keeps the AnomalyDetector project focused on domain logic, not data access plumbing
ExecuteUpdateAsync / ExecuteDeleteAsync
- Uses EF Core 7+ bulk update/delete operations
- More efficient than loading entity then saving
- Returns affected row count for existence checking
Timestamps
CreatedAtset on createUpdatedAtset on update operations- UTC times used consistently
Logging
- Info level for successful operations
- Warning level for not-found scenarios
Ordering
- Results ordered by
Namefor predictable iteration
Acceptance Criteria
- [ ]
EfMonitorSpecRepositoryclass created inKds.Ai.Data/Repositories/(sealed) - [ ] Namespace is
Kds.Ai.Data.Repositories - [ ] All interface methods implemented
- [ ] Uses
MonitorStateenum (notbool IsEnabled) - [ ] No references to
StoreGuidorMonitorType - [ ] Uses
ExecuteUpdateAsyncfor partial updates - [ ] Uses
ExecuteDeleteAsyncfor bulk delete - [ ] Timestamps managed correctly
- [ ] Logging integrated
- [ ] Repository registered in
AddDataModuleinKds.Ai.Data/DependencyInjectionExtensions.cs - [ ] No repository registration needed in AnomalyDetector's DI
- [ ] Solution builds without errors
Testing Considerations
csharp
// Example usage in tests - resolve from any project that calls AddDataModule
var repo = serviceProvider.GetRequiredService<IMonitorSpecRepository>();
// Create
var spec = await repo.CreateAsync(new MonitorSpec
{
Name = "Prep Time Monitor - Downtown",
SystemPrompt = "You are monitoring prep times...",
State = MonitorState.Active
});
// Query
var active = await repo.GetActiveAsync();
Assert.Contains(active, s => s.Name == "Prep Time Monitor - Downtown");
// State transition
await repo.SetStateAsync(spec.Id, MonitorState.Paused);
// Delete
var deleted = await repo.DeleteAsync(spec.Id);
Assert.True(deleted);Notes
- The implementation assumes a scoped
KdsAiDbContextfrom DI (registered byAddDataModule) - No explicit transactions - rely on EF Core's default behavior
- Consider adding concurrency token if needed
- Consumer projects (e.g., AnomalyDetector) inject
IMonitorSpecRepository- they don't need to know it's EF Core behind the scenes