sprints work

This commit is contained in:
master
2026-01-10 20:32:13 +02:00
parent 0d5eda86fc
commit 17d0631b8e
189 changed files with 40667 additions and 497 deletions

View File

@@ -14,6 +14,7 @@ public sealed class DecisionService : IDecisionService
{
private readonly ILedgerEventWriteService _writeService;
private readonly ILedgerEventRepository _repository;
private readonly IEnumerable<IDecisionHook> _hooks;
private readonly TimeProvider _timeProvider;
private readonly ILogger<DecisionService> _logger;
@@ -22,11 +23,13 @@ public sealed class DecisionService : IDecisionService
public DecisionService(
ILedgerEventWriteService writeService,
ILedgerEventRepository repository,
IEnumerable<IDecisionHook> hooks,
TimeProvider timeProvider,
ILogger<DecisionService> logger)
{
_writeService = writeService ?? throw new ArgumentNullException(nameof(writeService));
_repository = repository ?? throw new ArgumentNullException(nameof(repository));
_hooks = hooks ?? Enumerable.Empty<IDecisionHook>();
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
@@ -111,9 +114,37 @@ public sealed class DecisionService : IDecisionService
"Decision {DecisionId} recorded for alert {AlertId}: {Status}",
decision.Id, decision.AlertId, decision.DecisionStatus);
// Fire-and-forget hooks - don't block the caller
_ = FireHooksAsync(decision, tenantId, cancellationToken);
return decision;
}
/// <summary>
/// Fires all registered hooks asynchronously.
/// </summary>
private async Task FireHooksAsync(
DecisionEvent decision,
string tenantId,
CancellationToken cancellationToken)
{
foreach (var hook in _hooks)
{
try
{
await hook.OnDecisionRecordedAsync(decision, tenantId, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
// Hooks are fire-and-forget; log but don't throw
_logger.LogWarning(
ex,
"Decision hook {HookType} failed for decision {DecisionId}: {Message}",
hook.GetType().Name, decision.Id, ex.Message);
}
}
}
/// <summary>
/// Gets decision history for an alert (immutable timeline).
/// </summary>

View File

@@ -0,0 +1,56 @@
// <copyright file="IDecisionHook.cs" company="StellaOps">
// Copyright (c) StellaOps. Licensed under the AGPL-3.0-or-later.
// </copyright>
using StellaOps.Findings.Ledger.Domain;
namespace StellaOps.Findings.Ledger.Services;
/// <summary>
/// Hook interface for decision recording events.
/// Implementations are called asynchronously after decision is persisted.
/// </summary>
/// <remarks>
/// Hooks are fire-and-forget; exceptions are logged but don't block the caller.
/// SPRINT_20260107_006_004_BE Task: OM-007
/// </remarks>
public interface IDecisionHook
{
/// <summary>
/// Called after a decision is recorded to the ledger.
/// </summary>
/// <param name="decision">The recorded decision event.</param>
/// <param name="tenantId">The tenant identifier.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task representing the asynchronous operation.</returns>
Task OnDecisionRecordedAsync(
DecisionEvent decision,
string tenantId,
CancellationToken cancellationToken = default);
}
/// <summary>
/// Context provided to decision hooks for enriched processing.
/// </summary>
public sealed record DecisionHookContext
{
/// <summary>
/// The decision event that was recorded.
/// </summary>
public required DecisionEvent Decision { get; init; }
/// <summary>
/// The tenant identifier.
/// </summary>
public required string TenantId { get; init; }
/// <summary>
/// When the decision was persisted to the ledger.
/// </summary>
public required DateTimeOffset PersistedAt { get; init; }
/// <summary>
/// The ledger event sequence number, if available.
/// </summary>
public long? SequenceNumber { get; init; }
}