diff --git a/src/dotnet/QuantEngine.Application/Services/KisDataCollectionOrchestrator.cs b/src/dotnet/QuantEngine.Application/Services/KisDataCollectionOrchestrator.cs index c6da4209..2a4d43a7 100644 --- a/src/dotnet/QuantEngine.Application/Services/KisDataCollectionOrchestrator.cs +++ b/src/dotnet/QuantEngine.Application/Services/KisDataCollectionOrchestrator.cs @@ -168,6 +168,7 @@ public class KisDataCollectionOrchestrator : ICollectionOrchestrator } }; File.WriteAllText(outputPath, JsonSerializer.Serialize(outputData, new JsonSerializerOptions { WriteIndented = true })); + LogLineageEvent(runId, result.Status, result.SuccessCount, result.ErrorCount); _logger.LogInformation("Collection run {RunId} finished with status {Status}: {Success} ok, {Errors} errors", runId, result.Status, result.SuccessCount, result.ErrorCount); @@ -220,6 +221,46 @@ public class KisDataCollectionOrchestrator : ICollectionOrchestrator return false; } + + private static void LogLineageEvent(string runId, string status, int successCount, int errorCount) + { + try + { + var baseDir = AppContext.BaseDirectory; + var current = new DirectoryInfo(baseDir); + string? repoRoot = null; + + while (current != null) + { + if (Directory.Exists(Path.Combine(current.FullName, ".git"))) + { + repoRoot = current.FullName; + break; + } + current = current.Parent; + } + + if (repoRoot != null) + { + var runtimeDir = Path.Combine(repoRoot, "runtime"); + Directory.CreateDirectory(runtimeDir); + var lineagePath = Path.Combine(runtimeDir, "lineage_events.jsonl"); + + var ev = new + { + @event = "collection_run_completed", + run_id = runId, + status = status, + success_count = successCount, + error_count = errorCount, + timestamp = DataNormalizationHelper.KstNowIso() + }; + + File.AppendAllText(lineagePath, JsonSerializer.Serialize(ev) + "\n"); + } + } + catch { /* Robust fallback */ } + } }