Code

Get started

Record two source histories, then compare what they saw.

The shortest useful path: define one state window, ingest events from two sources, compare the recorded windows, and inspect the output.

Mental model

Start with one question: when was this state active?

A window opens when your predicate becomes true for a key and source. It closes when the predicate becomes false. Once those windows are recorded, compare sources without hand-written interval joins.

1. Install

Add the package to your project.

dotnet add package Spanfold
cd packages/python
python -m pip install -e ".[dev]"

The Python package includes the core runtime, comparison, export, testing, and CLI surfaces.

2. Minimal implementation

A complete first example.

This example uses processing positions rather than timestamps so the output is easy to inspect. Position 1 is the first ingested event, position 2 is the second, and so on.

using Spanfold;

var pipeline = Spanfold.Spanfold
    .For<DeviceStatus>()
    .RecordWindows()
    .TrackWindow(
        "DeviceOffline",
        key: status => status.DeviceId,
        isActive: status => !status.IsOnline);

Ingest("provider-a", isOnline: true);  // position 1
Ingest("provider-b", isOnline: true);  // position 2
Ingest("provider-a", isOnline: false); // position 3, provider-a opens
Ingest("provider-b", isOnline: false); // position 4, provider-b opens
Ingest("provider-b", isOnline: true);  // position 5, provider-b closes
Ingest("provider-a", isOnline: true);  // position 6, provider-a closes

var result = pipeline.History
    .Compare("Provider comparison")
    .Target("provider-a", selector => selector.Source("provider-a"))
    .Against("provider-b", selector => selector.Source("provider-b"))
    .Within(scope => scope.Window("DeviceOffline"))
    .Using(comparators => comparators.Overlap().Residual().Missing())
    .Run();

Console.WriteLine($"closed windows: {pipeline.History.ClosedWindows.Count}");
Console.WriteLine($"overlap rows: {result.OverlapRows.Count}");
Console.WriteLine($"provider-a-only rows: {result.ResidualRows.Count}");
Console.WriteLine($"provider-b-only rows: {result.MissingRows.Count}");

foreach (var row in result.OverlapRows)
{
    Console.WriteLine($"overlap {row.Key}: {row.Range.Start.Position}..{row.Range.End!.Value.Position}");
}

foreach (var row in result.ResidualRows)
{
    Console.WriteLine($"a-only {row.Key}: {row.Range.Start.Position}..{row.Range.End!.Value.Position}");
}

void Ingest(string source, bool isOnline)
{
    pipeline.Ingest(new DeviceStatus("device-17", isOnline), source);
}

public sealed record DeviceStatus(string DeviceId, bool IsOnline);
from dataclasses import dataclass

from spanfold import Spanfold


@dataclass(frozen=True)
class DeviceStatus:
    device_id: str
    is_online: bool


pipeline = (
    Spanfold.for_events()
    .record_windows()
    .track_window(
        "DeviceOffline",
        key=lambda status: status.device_id,
        is_active=lambda status: not status.is_online,
    )
)

pipeline.ingest(DeviceStatus("device-17", True), source="provider-a")
pipeline.ingest(DeviceStatus("device-17", True), source="provider-b")
pipeline.ingest(DeviceStatus("device-17", False), source="provider-a")
pipeline.ingest(DeviceStatus("device-17", False), source="provider-b")
pipeline.ingest(DeviceStatus("device-17", True), source="provider-b")
pipeline.ingest(DeviceStatus("device-17", True), source="provider-a")

result = (
    pipeline.history.compare("Provider comparison")
    .target("provider-a")
    .against("provider-b")
    .within(window_name="DeviceOffline")
    .using("overlap", "residual", "missing")
    .run()
)

print(f"closed windows: {len(pipeline.history.closed_windows)}")
print(f"overlap rows: {len(result.overlap_rows)}")
print(f"provider-a-only rows: {len(result.residual_rows)}")
print(f"provider-b-only rows: {len(result.missing_rows)}")

3. Expected output

The result should show one shared segment and two target-only segments.

closed windows: 2
overlap rows: 1
provider-a-only rows: 2
provider-b-only rows: 0
overlap device-17: 4..5
a-only device-17: 3..4
a-only device-17: 5..6

Provider A reported the device offline from position 3 to 6. Provider B reported it offline from position 4 to 5. Spanfold turns that into structured comparison rows: A-only before B saw it, overlap while both saw it, and A-only after B recovered.

4. What to try next

Move from the smallest case to useful diagnostics.