feat: Add new projects to solution and implement contract testing documentation

- Added "StellaOps.Policy.Engine", "StellaOps.Cartographer", and "StellaOps.SbomService" projects to the StellaOps solution.
- Created AGENTS.md to outline the Contract Testing Guild Charter, detailing mission, scope, and definition of done.
- Established TASKS.md for the Contract Testing Task Board, outlining tasks for Sprint 62 and Sprint 63 related to mock servers and replay testing.
This commit is contained in:
master
2025-10-27 07:57:55 +02:00
parent 935ec9aa25
commit 2b7b88ca77
355 changed files with 17276 additions and 1160 deletions

View File

@@ -0,0 +1,28 @@
# Tenant Timeline Indexer — Agent Charter
## Mission
Build the tenant-scoped timeline ingestion and query service described in Epic 15. Consume structured timeline events from all services, maintain queryable indices, and expose APIs to Console and CLI without violating imposed rule guarantees.
## Responsibilities
- Define Postgres schema, RLS policies, and ingestion pipelines for `timeline_events`.
- Provide event consumers for NATS/Redis queues with dedupe + ordering logic.
- Serve REST/gRPC APIs powering Console Forensics Explorer and CLI `stella obs trace`/`timeline` flows.
- Emit metrics/traces/logs for ingestion health and query performance.
## Collaboration
- Coordinate with Telemetry Core for event schema definitions.
- Work with Evidence Locker to link events to evidence bundle digests.
- Align with Authority on new `timeline:read` scopes and tenant enforcement.
## Definition of Done
- Service ships with deterministic migrations + repeatable seeds.
- Integration tests replay recorded event fixtures to stable results.
- Docs updated under `/docs/forensics/timeline.md` per release.
## Module Layout
- `StellaOps.TimelineIndexer.Core/` — event models, ordering/dedupe logic, query contracts.
- `StellaOps.TimelineIndexer.Infrastructure/` — Postgres/NATS clients, persistence abstractions.
- `StellaOps.TimelineIndexer.WebService/` — query/lookup APIs and authentication glue.
- `StellaOps.TimelineIndexer.Worker/` — ingestion consumers and background compaction jobs.
- `StellaOps.TimelineIndexer.Tests/` — unit tests focused on ordering/dedupe/query correctness.
- `StellaOps.TimelineIndexer.sln` — solution aggregating module projects.

View File

@@ -0,0 +1,6 @@
namespace StellaOps.TimelineIndexer.Core;
public class Class1
{
}

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" ?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,6 @@
namespace StellaOps.TimelineIndexer.Infrastructure;
public class Class1
{
}

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" ?>
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\StellaOps.TimelineIndexer.Core\StellaOps.TimelineIndexer.Core.csproj"/>
</ItemGroup>
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,135 @@
<?xml version="1.0" ?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<IsPackable>false</IsPackable>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UseConcelierTestInfra>false</UseConcelierTestInfra>
<LangVersion>preview</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1"/>
<PackageReference Include="xunit.v3" Version="3.0.0"/>
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.3"/>
</ItemGroup>
<ItemGroup>
<Content Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest"/>
</ItemGroup>
<ItemGroup>
<Using Include="Xunit"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\StellaOps.TimelineIndexer.Core\StellaOps.TimelineIndexer.Core.csproj"/>
<ProjectReference Include="..\StellaOps.TimelineIndexer.Infrastructure\StellaOps.TimelineIndexer.Infrastructure.csproj"/>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,10 @@
namespace StellaOps.TimelineIndexer.Tests;
public class UnitTest1
{
[Fact]
public void Test1()
{
}
}

View File

@@ -0,0 +1,3 @@
{
"$schema": "https://xunit.net/schema/current/xunit.runner.schema.json"
}

View File

@@ -0,0 +1,41 @@
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder.Services.AddOpenApi();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.UseHttpsRedirection();
var summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
app.MapGet("/weatherforecast", () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
})
.WithName("GetWeatherForecast");
app.Run();
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

View File

@@ -0,0 +1,23 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "http://localhost:5194",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "https://localhost:7272;http://localhost:5194",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" ?>
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0-rc.1.25451.107"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\StellaOps.TimelineIndexer.Core\StellaOps.TimelineIndexer.Core.csproj"/>
<ProjectReference Include="..\StellaOps.TimelineIndexer.Infrastructure\StellaOps.TimelineIndexer.Infrastructure.csproj"/>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,6 @@
@StellaOps.TimelineIndexer.WebService_HostAddress = http://localhost:5194
GET {{StellaOps.TimelineIndexer.WebService_HostAddress}}/weatherforecast/
Accept: application/json
###

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@@ -0,0 +1,7 @@
using StellaOps.TimelineIndexer.Worker;
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<Worker>();
var host = builder.Build();
host.Run();

View File

@@ -0,0 +1,12 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"StellaOps.TimelineIndexer.Worker": {
"commandName": "Project",
"dotnetRunMessages": true,
"environmentVariables": {
"DOTNET_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" ?>
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<UserSecretsId>dotnet-StellaOps.TimelineIndexer.Worker-f6dbdeac-9eb5-4250-9384-ef93fc70f770</UserSecretsId>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0-rc.1.25451.107"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\StellaOps.TimelineIndexer.Core\StellaOps.TimelineIndexer.Core.csproj"/>
<ProjectReference Include="..\StellaOps.TimelineIndexer.Infrastructure\StellaOps.TimelineIndexer.Infrastructure.csproj"/>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,16 @@
namespace StellaOps.TimelineIndexer.Worker;
public class Worker(ILogger<Worker> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
if (logger.IsEnabled(LogLevel.Information))
{
logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
}
await Task.Delay(1000, stoppingToken);
}
}
}

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

View File

@@ -0,0 +1,90 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.TimelineIndexer.Core", "StellaOps.TimelineIndexer.Core\StellaOps.TimelineIndexer.Core.csproj", "{C8959267-ACDD-49E9-B1FD-9694C8663437}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.TimelineIndexer.Infrastructure", "StellaOps.TimelineIndexer.Infrastructure\StellaOps.TimelineIndexer.Infrastructure.csproj", "{185CEED8-197F-4236-8716-73B37C5F355A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.TimelineIndexer.WebService", "StellaOps.TimelineIndexer.WebService\StellaOps.TimelineIndexer.WebService.csproj", "{991C4CD2-F5D2-4AB7-83A5-EF4E60B61A86}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.TimelineIndexer.Worker", "StellaOps.TimelineIndexer.Worker\StellaOps.TimelineIndexer.Worker.csproj", "{B8F1FE1E-7730-431D-B058-9C7A50463F91}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.TimelineIndexer.Tests", "StellaOps.TimelineIndexer.Tests\StellaOps.TimelineIndexer.Tests.csproj", "{AA20938D-A0AC-4E37-B7D9-002C6DD90FEC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C8959267-ACDD-49E9-B1FD-9694C8663437}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C8959267-ACDD-49E9-B1FD-9694C8663437}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C8959267-ACDD-49E9-B1FD-9694C8663437}.Debug|x64.ActiveCfg = Debug|Any CPU
{C8959267-ACDD-49E9-B1FD-9694C8663437}.Debug|x64.Build.0 = Debug|Any CPU
{C8959267-ACDD-49E9-B1FD-9694C8663437}.Debug|x86.ActiveCfg = Debug|Any CPU
{C8959267-ACDD-49E9-B1FD-9694C8663437}.Debug|x86.Build.0 = Debug|Any CPU
{C8959267-ACDD-49E9-B1FD-9694C8663437}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C8959267-ACDD-49E9-B1FD-9694C8663437}.Release|Any CPU.Build.0 = Release|Any CPU
{C8959267-ACDD-49E9-B1FD-9694C8663437}.Release|x64.ActiveCfg = Release|Any CPU
{C8959267-ACDD-49E9-B1FD-9694C8663437}.Release|x64.Build.0 = Release|Any CPU
{C8959267-ACDD-49E9-B1FD-9694C8663437}.Release|x86.ActiveCfg = Release|Any CPU
{C8959267-ACDD-49E9-B1FD-9694C8663437}.Release|x86.Build.0 = Release|Any CPU
{185CEED8-197F-4236-8716-73B37C5F355A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{185CEED8-197F-4236-8716-73B37C5F355A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{185CEED8-197F-4236-8716-73B37C5F355A}.Debug|x64.ActiveCfg = Debug|Any CPU
{185CEED8-197F-4236-8716-73B37C5F355A}.Debug|x64.Build.0 = Debug|Any CPU
{185CEED8-197F-4236-8716-73B37C5F355A}.Debug|x86.ActiveCfg = Debug|Any CPU
{185CEED8-197F-4236-8716-73B37C5F355A}.Debug|x86.Build.0 = Debug|Any CPU
{185CEED8-197F-4236-8716-73B37C5F355A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{185CEED8-197F-4236-8716-73B37C5F355A}.Release|Any CPU.Build.0 = Release|Any CPU
{185CEED8-197F-4236-8716-73B37C5F355A}.Release|x64.ActiveCfg = Release|Any CPU
{185CEED8-197F-4236-8716-73B37C5F355A}.Release|x64.Build.0 = Release|Any CPU
{185CEED8-197F-4236-8716-73B37C5F355A}.Release|x86.ActiveCfg = Release|Any CPU
{185CEED8-197F-4236-8716-73B37C5F355A}.Release|x86.Build.0 = Release|Any CPU
{991C4CD2-F5D2-4AB7-83A5-EF4E60B61A86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{991C4CD2-F5D2-4AB7-83A5-EF4E60B61A86}.Debug|Any CPU.Build.0 = Debug|Any CPU
{991C4CD2-F5D2-4AB7-83A5-EF4E60B61A86}.Debug|x64.ActiveCfg = Debug|Any CPU
{991C4CD2-F5D2-4AB7-83A5-EF4E60B61A86}.Debug|x64.Build.0 = Debug|Any CPU
{991C4CD2-F5D2-4AB7-83A5-EF4E60B61A86}.Debug|x86.ActiveCfg = Debug|Any CPU
{991C4CD2-F5D2-4AB7-83A5-EF4E60B61A86}.Debug|x86.Build.0 = Debug|Any CPU
{991C4CD2-F5D2-4AB7-83A5-EF4E60B61A86}.Release|Any CPU.ActiveCfg = Release|Any CPU
{991C4CD2-F5D2-4AB7-83A5-EF4E60B61A86}.Release|Any CPU.Build.0 = Release|Any CPU
{991C4CD2-F5D2-4AB7-83A5-EF4E60B61A86}.Release|x64.ActiveCfg = Release|Any CPU
{991C4CD2-F5D2-4AB7-83A5-EF4E60B61A86}.Release|x64.Build.0 = Release|Any CPU
{991C4CD2-F5D2-4AB7-83A5-EF4E60B61A86}.Release|x86.ActiveCfg = Release|Any CPU
{991C4CD2-F5D2-4AB7-83A5-EF4E60B61A86}.Release|x86.Build.0 = Release|Any CPU
{B8F1FE1E-7730-431D-B058-9C7A50463F91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B8F1FE1E-7730-431D-B058-9C7A50463F91}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B8F1FE1E-7730-431D-B058-9C7A50463F91}.Debug|x64.ActiveCfg = Debug|Any CPU
{B8F1FE1E-7730-431D-B058-9C7A50463F91}.Debug|x64.Build.0 = Debug|Any CPU
{B8F1FE1E-7730-431D-B058-9C7A50463F91}.Debug|x86.ActiveCfg = Debug|Any CPU
{B8F1FE1E-7730-431D-B058-9C7A50463F91}.Debug|x86.Build.0 = Debug|Any CPU
{B8F1FE1E-7730-431D-B058-9C7A50463F91}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B8F1FE1E-7730-431D-B058-9C7A50463F91}.Release|Any CPU.Build.0 = Release|Any CPU
{B8F1FE1E-7730-431D-B058-9C7A50463F91}.Release|x64.ActiveCfg = Release|Any CPU
{B8F1FE1E-7730-431D-B058-9C7A50463F91}.Release|x64.Build.0 = Release|Any CPU
{B8F1FE1E-7730-431D-B058-9C7A50463F91}.Release|x86.ActiveCfg = Release|Any CPU
{B8F1FE1E-7730-431D-B058-9C7A50463F91}.Release|x86.Build.0 = Release|Any CPU
{AA20938D-A0AC-4E37-B7D9-002C6DD90FEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AA20938D-A0AC-4E37-B7D9-002C6DD90FEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AA20938D-A0AC-4E37-B7D9-002C6DD90FEC}.Debug|x64.ActiveCfg = Debug|Any CPU
{AA20938D-A0AC-4E37-B7D9-002C6DD90FEC}.Debug|x64.Build.0 = Debug|Any CPU
{AA20938D-A0AC-4E37-B7D9-002C6DD90FEC}.Debug|x86.ActiveCfg = Debug|Any CPU
{AA20938D-A0AC-4E37-B7D9-002C6DD90FEC}.Debug|x86.Build.0 = Debug|Any CPU
{AA20938D-A0AC-4E37-B7D9-002C6DD90FEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AA20938D-A0AC-4E37-B7D9-002C6DD90FEC}.Release|Any CPU.Build.0 = Release|Any CPU
{AA20938D-A0AC-4E37-B7D9-002C6DD90FEC}.Release|x64.ActiveCfg = Release|Any CPU
{AA20938D-A0AC-4E37-B7D9-002C6DD90FEC}.Release|x64.Build.0 = Release|Any CPU
{AA20938D-A0AC-4E37-B7D9-002C6DD90FEC}.Release|x86.ActiveCfg = Release|Any CPU
{AA20938D-A0AC-4E37-B7D9-002C6DD90FEC}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,14 @@
# Timeline Indexer Task Board — Epic 15: Observability & Forensics
## Sprint 52 Timeline Foundations
| ID | Status | Owner(s) | Depends on | Description | Exit Criteria |
|----|--------|----------|------------|-------------|---------------|
| TIMELINE-OBS-52-001 | TODO | Timeline Indexer Guild | TELEMETRY-OBS-50-001, AUTH-OBS-50-001 | Bootstrap `StellaOps.Timeline.Indexer` service with Postgres migrations for `timeline_events`, `timeline_event_details`, `timeline_event_digests`; enable RLS scaffolding and deterministic migration scripts. | Service builds/tests; migrations replay cleanly; baseline seed fixtures committed; compliance checklist recorded. |
| TIMELINE-OBS-52-002 | TODO | Timeline Indexer Guild | TIMELINE-OBS-52-001, DEVOPS-OBS-50-002 | Implement event ingestion pipeline (NATS/Redis consumers) with ordering guarantees, dedupe on `(event_id, tenant_id)`, correlation to trace IDs, and backpressure metrics. | Ingestion integration tests replay fixture stream; dedupe proven; metrics exposed; failure retries documented. |
| TIMELINE-OBS-52-003 | TODO | Timeline Indexer Guild | TIMELINE-OBS-52-002 | Expose REST/gRPC APIs for timeline queries (`GET /timeline`, `/timeline/{id}`) with filters, pagination, and tenant enforcement. Provide OpenAPI + contract tests. | APIs documented via OpenAPI; tests cover filters/pagination; latency budget <200ms P95 on seeded data; audit logs recorded. |
| TIMELINE-OBS-52-004 | TODO | Timeline Indexer Guild, Security Guild | TIMELINE-OBS-52-001 | Finalize RLS policies, scope checks (`timeline:read`), and audit logging for query access. Include integration tests for cross-tenant isolation and legal hold markers. | RLS proven with failing cross-tenant queries; audit logs include actor/tenant; legal hold flag prevents deletion; docs referenced. |
## Sprint 53 Evidence & Provenance Integration
| ID | Status | Owner(s) | Depends on | Description | Exit Criteria |
|----|--------|----------|------------|-------------|---------------|
| TIMELINE-OBS-53-001 | TODO | Timeline Indexer Guild, Evidence Locker Guild | TIMELINE-OBS-52-003, EVID-OBS-53-002 | Link timeline events to evidence bundle digests + attestation subjects; expose `/timeline/{id}/evidence` endpoint returning signed manifest references. | Endpoint returns evidence references with DSSE metadata; integration test verifies digest match; docs updated. |