feat: preserve due operation provenance (AEG-V15-035)
Carries scheduledFor, catch-up policy, and maxCatchUp from the scheduler through the request model and transactional outbox. Evidence: targeted Release tests 5/5 passed; TRX SHA256 C1BF3EF274702305A29673D5B6A1C3A98D08B1716DA3CD8CB0EE710B5E6C12E6. Schedules remain disabled.
This commit is contained in:
@@ -14,6 +14,9 @@ public sealed class ModelOperationRequestService(
|
||||
string scopeKey,
|
||||
string automationMode,
|
||||
string idempotencyKey,
|
||||
DateTimeOffset scheduledFor,
|
||||
string catchUpPolicy,
|
||||
int maxCatchUp,
|
||||
string correlationId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -21,6 +24,8 @@ public sealed class ModelOperationRequestService(
|
||||
ModelOperationExecutionBoundary.EnsureAllowed(definition);
|
||||
if (!definition.AutomationMode.ToContractValue().Equals(automationMode, StringComparison.OrdinalIgnoreCase))
|
||||
throw new InvalidOperationException("Schedule automation mode does not match the approved operation registry.");
|
||||
if (maxCatchUp is < 0 or > 31) throw new ArgumentOutOfRangeException(nameof(maxCatchUp));
|
||||
if (string.IsNullOrWhiteSpace(catchUpPolicy)) throw new ArgumentException("Catch-up policy is required.", nameof(catchUpPolicy));
|
||||
|
||||
var now = clock.UtcNow;
|
||||
var context = await contextReader.ReadAsync(scopeKey, now, cancellationToken);
|
||||
@@ -35,6 +40,9 @@ public sealed class ModelOperationRequestService(
|
||||
scopeKey,
|
||||
definition.AutomationMode.ToContractValue(),
|
||||
idempotencyKey,
|
||||
scheduledFor,
|
||||
catchUpPolicy,
|
||||
maxCatchUp,
|
||||
context,
|
||||
correlationId,
|
||||
now);
|
||||
|
||||
@@ -28,6 +28,9 @@ public sealed record ModelOperationRequest(
|
||||
string ScopeKey,
|
||||
string AutomationMode,
|
||||
string IdempotencyKey,
|
||||
DateTimeOffset ScheduledFor,
|
||||
string CatchUpPolicy,
|
||||
int MaxCatchUp,
|
||||
ApprovedModelContext Context,
|
||||
string CorrelationId,
|
||||
DateTimeOffset RequestedAt);
|
||||
@@ -82,6 +85,9 @@ public interface IModelOperationRequestService
|
||||
string scopeKey,
|
||||
string automationMode,
|
||||
string idempotencyKey,
|
||||
DateTimeOffset scheduledFor,
|
||||
string catchUpPolicy,
|
||||
int maxCatchUp,
|
||||
string correlationId,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
+6
-2
@@ -15,11 +15,11 @@ public sealed class DapperModelOperationRequestRepository(
|
||||
insert into evaluation.model_operation_request
|
||||
(request_id, schedule_id, operation_code, scope_key, automation_mode, idempotency_key,
|
||||
dataset_id, data_hash, model_version, config_version, code_sha, contract_version,
|
||||
lifecycle_state, correlation_id, status, requested_at)
|
||||
lifecycle_state, correlation_id, status, requested_at, scheduled_for)
|
||||
values
|
||||
(@RequestId, @ScheduleId, @OperationCode, @ScopeKey, @AutomationMode, @IdempotencyKey,
|
||||
@DatasetId, @DataHash, @ModelVersion, @ConfigVersion, @CodeSha, @ContractVersion,
|
||||
@LifecycleState, @CorrelationId, 'REQUESTED', @RequestedAt)
|
||||
@LifecycleState, @CorrelationId, 'REQUESTED', @RequestedAt, @ScheduledFor)
|
||||
on conflict (idempotency_key) do nothing;
|
||||
""";
|
||||
|
||||
@@ -47,6 +47,7 @@ public sealed class DapperModelOperationRequestRepository(
|
||||
request.ScopeKey,
|
||||
request.AutomationMode,
|
||||
request.IdempotencyKey,
|
||||
request.ScheduledFor,
|
||||
request.Context.VersionSet.DatasetId,
|
||||
request.Context.VersionSet.DataHash,
|
||||
request.Context.VersionSet.ModelVersion,
|
||||
@@ -68,6 +69,9 @@ public sealed class DapperModelOperationRequestRepository(
|
||||
request.OperationCode,
|
||||
request.ScopeKey,
|
||||
request.AutomationMode,
|
||||
request.ScheduledFor,
|
||||
request.CatchUpPolicy,
|
||||
request.MaxCatchUp,
|
||||
versionSet = request.Context.VersionSet,
|
||||
boundary = "NO_AUTO_MODEL_MUTATION"
|
||||
});
|
||||
|
||||
@@ -49,7 +49,10 @@ public sealed class ModelOperationsDispatcherJob(
|
||||
item.OperationCode,
|
||||
item.ScopeKey,
|
||||
item.AutomationMode,
|
||||
idempotencyKey));
|
||||
idempotencyKey,
|
||||
occurrence,
|
||||
item.CatchUpPolicy,
|
||||
item.MaxCatchUp));
|
||||
backgroundJobId = jobs.Create(job, new EnqueuedState(item.Queue));
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,10 @@ public sealed class ScheduledModelOperationJob(
|
||||
string operationCode,
|
||||
string scopeKey,
|
||||
string automationMode,
|
||||
string idempotencyKey)
|
||||
string idempotencyKey,
|
||||
DateTimeOffset scheduledFor,
|
||||
string catchUpPolicy,
|
||||
int maxCatchUp)
|
||||
{
|
||||
var correlationId = $"model-operation:{operationCode}:{Guid.NewGuid():N}";
|
||||
var request = await service.RequestAsync(
|
||||
@@ -35,6 +38,9 @@ public sealed class ScheduledModelOperationJob(
|
||||
scopeKey,
|
||||
automationMode,
|
||||
idempotencyKey,
|
||||
scheduledFor,
|
||||
catchUpPolicy,
|
||||
maxCatchUp,
|
||||
correlationId,
|
||||
CancellationToken.None);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user