Repair live unified search corpus runtime

This commit is contained in:
master
2026-03-09 19:44:16 +02:00
parent bf937c9395
commit 00bf2fa99a
12 changed files with 175 additions and 38 deletions

View File

@@ -8,6 +8,15 @@ internal sealed record KnowledgeSearchRepositoryRootResolution(
internal static class KnowledgeSearchRepositoryRootResolver
{
public static string ResolvePath(KnowledgeSearchOptions options, string configuredPath)
{
return ResolvePath(options, configuredPath, Directory.GetCurrentDirectory(), AppContext.BaseDirectory);
}
internal static string ResolvePath(
KnowledgeSearchOptions options,
string configuredPath,
string? currentDirectory,
string? appBaseDirectory)
{
ArgumentNullException.ThrowIfNull(options);
ArgumentException.ThrowIfNullOrWhiteSpace(configuredPath);
@@ -17,7 +26,7 @@ internal static class KnowledgeSearchRepositoryRootResolver
return Path.GetFullPath(configuredPath);
}
var repositoryRoot = Resolve(options);
var repositoryRoot = Resolve(options, currentDirectory, appBaseDirectory);
return Path.GetFullPath(Path.Combine(repositoryRoot.Path, configuredPath));
}

View File

@@ -95,15 +95,31 @@
</None>
<None Update="UnifiedSearch/Snapshots/findings.snapshot.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>UnifiedSearch/Snapshots/findings.snapshot.json</TargetPath>
<TargetPath>src/AdvisoryAI/StellaOps.AdvisoryAI/UnifiedSearch/Snapshots/findings.snapshot.json</TargetPath>
</None>
<None Update="UnifiedSearch/Snapshots/vex.snapshot.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>UnifiedSearch/Snapshots/vex.snapshot.json</TargetPath>
<TargetPath>src/AdvisoryAI/StellaOps.AdvisoryAI/UnifiedSearch/Snapshots/vex.snapshot.json</TargetPath>
</None>
<None Update="UnifiedSearch/Snapshots/policy.snapshot.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>UnifiedSearch/Snapshots/policy.snapshot.json</TargetPath>
<TargetPath>src/AdvisoryAI/StellaOps.AdvisoryAI/UnifiedSearch/Snapshots/policy.snapshot.json</TargetPath>
</None>
<None Update="UnifiedSearch/Snapshots/graph.snapshot.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>src/AdvisoryAI/StellaOps.AdvisoryAI/UnifiedSearch/Snapshots/graph.snapshot.json</TargetPath>
</None>
<None Update="UnifiedSearch/Snapshots/opsmemory.snapshot.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>src/AdvisoryAI/StellaOps.AdvisoryAI/UnifiedSearch/Snapshots/opsmemory.snapshot.json</TargetPath>
</None>
<None Update="UnifiedSearch/Snapshots/timeline.snapshot.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>src/AdvisoryAI/StellaOps.AdvisoryAI/UnifiedSearch/Snapshots/timeline.snapshot.json</TargetPath>
</None>
<None Update="UnifiedSearch/Snapshots/scanner.snapshot.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>src/AdvisoryAI/StellaOps.AdvisoryAI/UnifiedSearch/Snapshots/scanner.snapshot.json</TargetPath>
</None>
</ItemGroup>
<ItemGroup>

View File

@@ -176,13 +176,7 @@ internal sealed class GraphNodeIngestionAdapter : ISearchIngestionAdapter
private string ResolvePath(string configuredPath)
{
if (Path.IsPathRooted(configuredPath))
{
return configuredPath;
}
var root = string.IsNullOrWhiteSpace(_knowledgeOptions.RepositoryRoot) ? "." : _knowledgeOptions.RepositoryRoot;
return Path.GetFullPath(Path.Combine(root, configuredPath));
return KnowledgeSearchRepositoryRootResolver.ResolvePath(_knowledgeOptions, configuredPath);
}
private static string BuildEntityKey(
@@ -224,4 +218,3 @@ internal sealed class GraphNodeIngestionAdapter : ISearchIngestionAdapter
return raw is not null && DateTimeOffset.TryParse(raw, out var timestamp) ? timestamp : null;
}
}

View File

@@ -141,13 +141,7 @@ internal sealed class OpsDecisionIngestionAdapter : ISearchIngestionAdapter
private string ResolvePath(string configuredPath)
{
if (Path.IsPathRooted(configuredPath))
{
return configuredPath;
}
var root = string.IsNullOrWhiteSpace(_knowledgeOptions.RepositoryRoot) ? "." : _knowledgeOptions.RepositoryRoot;
return Path.GetFullPath(Path.Combine(root, configuredPath));
return KnowledgeSearchRepositoryRootResolver.ResolvePath(_knowledgeOptions, configuredPath);
}
private static string GuessSubjectType(string subjectRef)
@@ -234,4 +228,3 @@ internal sealed class OpsDecisionIngestionAdapter : ISearchIngestionAdapter
.ToArray();
}
}

View File

@@ -139,13 +139,7 @@ internal sealed class ScanResultIngestionAdapter : ISearchIngestionAdapter
private string ResolvePath(string configuredPath)
{
if (Path.IsPathRooted(configuredPath))
{
return configuredPath;
}
var root = string.IsNullOrWhiteSpace(_knowledgeOptions.RepositoryRoot) ? "." : _knowledgeOptions.RepositoryRoot;
return Path.GetFullPath(Path.Combine(root, configuredPath));
return KnowledgeSearchRepositoryRootResolver.ResolvePath(_knowledgeOptions, configuredPath);
}
private static string? ReadString(JsonElement obj, string propertyName)
@@ -187,4 +181,3 @@ internal sealed class ScanResultIngestionAdapter : ISearchIngestionAdapter
.ToArray();
}
}

View File

@@ -144,13 +144,7 @@ internal sealed partial class TimelineEventIngestionAdapter : ISearchIngestionAd
private string ResolvePath(string configuredPath)
{
if (Path.IsPathRooted(configuredPath))
{
return configuredPath;
}
var root = string.IsNullOrWhiteSpace(_knowledgeOptions.RepositoryRoot) ? "." : _knowledgeOptions.RepositoryRoot;
return Path.GetFullPath(Path.Combine(root, configuredPath));
return KnowledgeSearchRepositoryRootResolver.ResolvePath(_knowledgeOptions, configuredPath);
}
private static (string? EntityKey, string EntityType) ExtractEntity(string targetRef)

View File

@@ -55,6 +55,39 @@ public sealed class KnowledgeSearchRepositoryRootResolverTests
Assert.Equal(fixture.AppBaseDirectory, resolution.Path);
}
[Fact]
public void ResolvePath_points_all_default_unified_snapshot_paths_at_runtime_publish_corpus()
{
using var fixture = RuntimePublishLayoutFixture.Create();
var knowledgeOptions = new KnowledgeSearchOptions();
var unifiedOptions = new StellaOps.AdvisoryAI.UnifiedSearch.UnifiedSearchOptions();
var unrelatedCurrentDirectory = Path.Combine(fixture.WorkspaceRoot, "outside");
Directory.CreateDirectory(unrelatedCurrentDirectory);
var configuredPaths = new[]
{
knowledgeOptions.UnifiedFindingsSnapshotPath,
knowledgeOptions.UnifiedVexSnapshotPath,
knowledgeOptions.UnifiedPolicySnapshotPath,
unifiedOptions.Ingestion.GraphSnapshotPath,
unifiedOptions.Ingestion.OpsMemorySnapshotPath,
unifiedOptions.Ingestion.TimelineSnapshotPath,
unifiedOptions.Ingestion.ScannerSnapshotPath,
};
foreach (var configuredPath in configuredPaths)
{
var resolved = KnowledgeSearchRepositoryRootResolver.ResolvePath(
knowledgeOptions,
configuredPath,
unrelatedCurrentDirectory,
fixture.AppBaseDirectory);
Assert.StartsWith(fixture.AppBaseDirectory, resolved, StringComparison.OrdinalIgnoreCase);
Assert.True(File.Exists(resolved), $"Expected runtime corpus asset for {configuredPath} at {resolved}.");
}
}
private sealed class RepositoryRootFixture : IDisposable
{
private RepositoryRootFixture(
@@ -131,10 +164,12 @@ public sealed class KnowledgeSearchRepositoryRootResolverTests
{
var workspaceRoot = Path.Combine(Path.GetTempPath(), "stellaops-knowledge-runtime-" + Guid.NewGuid().ToString("N"));
var appBaseDirectory = Path.Combine(workspaceRoot, "app");
var snapshotDirectory = Path.Combine(appBaseDirectory, "src", "AdvisoryAI", "StellaOps.AdvisoryAI", "UnifiedSearch", "Snapshots");
Directory.CreateDirectory(Path.Combine(appBaseDirectory, "docs", "modules", "advisory-ai"));
Directory.CreateDirectory(Path.Combine(appBaseDirectory, "devops", "compose"));
Directory.CreateDirectory(Path.Combine(appBaseDirectory, "src", "AdvisoryAI", "StellaOps.AdvisoryAI", "KnowledgeSearch"));
Directory.CreateDirectory(snapshotDirectory);
File.WriteAllText(Path.Combine(appBaseDirectory, "docs", "README.md"), "# docs");
File.WriteAllText(Path.Combine(appBaseDirectory, "docs", "modules", "advisory-ai", "knowledge-search.md"), "# advisory");
@@ -148,6 +183,13 @@ public sealed class KnowledgeSearchRepositoryRootResolverTests
File.WriteAllText(
Path.Combine(appBaseDirectory, "src", "AdvisoryAI", "StellaOps.AdvisoryAI", "KnowledgeSearch", "doctor-search-controls.json"),
"[]");
File.WriteAllText(Path.Combine(snapshotDirectory, "findings.snapshot.json"), "[]");
File.WriteAllText(Path.Combine(snapshotDirectory, "vex.snapshot.json"), "[]");
File.WriteAllText(Path.Combine(snapshotDirectory, "policy.snapshot.json"), "[]");
File.WriteAllText(Path.Combine(snapshotDirectory, "graph.snapshot.json"), "[]");
File.WriteAllText(Path.Combine(snapshotDirectory, "opsmemory.snapshot.json"), "[]");
File.WriteAllText(Path.Combine(snapshotDirectory, "timeline.snapshot.json"), "[]");
File.WriteAllText(Path.Combine(snapshotDirectory, "scanner.snapshot.json"), "[]");
return new RuntimePublishLayoutFixture(workspaceRoot, appBaseDirectory);
}

View File

@@ -299,20 +299,24 @@ public sealed class UnifiedSearchIngestionAdaptersTests
try
{
Directory.SetCurrentDirectory(fixture.ServiceDirectory);
var knowledgeOptions = Options.Create(new KnowledgeSearchOptions
{
RepositoryRoot = fixture.RepositoryRoot
});
var findingsAdapter = new FindingIngestionAdapter(
new StubVectorEncoder(),
Options.Create(new KnowledgeSearchOptions()),
knowledgeOptions,
NullLogger<FindingIngestionAdapter>.Instance);
var policyAdapter = new PolicySearchAdapter(
new StubHttpClientFactory(),
new StubVectorEncoder(),
Options.Create(new KnowledgeSearchOptions()),
knowledgeOptions,
NullLogger<PolicySearchAdapter>.Instance);
var vexAdapter = new VexSearchAdapter(
new StubHttpClientFactory(),
new StubVectorEncoder(),
Options.Create(new KnowledgeSearchOptions()),
knowledgeOptions,
NullLogger<VexSearchAdapter>.Instance);
var findings = await findingsAdapter.ProduceChunksAsync(CancellationToken.None);