Concepts

Split windows only when context changes the analysis.

Segments are boundary context: when a segment value changes, the active window is split. Tags are descriptive context: they travel with the window without creating a new interval.

Problem

Not every label should become a new key.

Operational state often changes context while staying active. A device can remain offline while an incident moves from triage to escalated. If that context matters analytically, one long window is too coarse. If it does not create a boundary, splitting the window creates noise.

Solution

Use partitions, segments, and tags for different jobs.

Partition isolates runtime state. Segment changes split an active window and can be used in comparison scope. Tags attach descriptive metadata without creating a boundary.

.Window("DeviceOffline", window => window
    .Key(update => update.DeviceId)
    .ActiveWhen(update => update.IsOffline)
    .Segment("lifecycle", lifecycle => lifecycle
        .Value(update => update.Lifecycle)
        .Child("stage", stage => stage.Value(update => update.Stage)))
    .Tag("fleet", update => update.FleetId))

How it works

Segment changes close and reopen the active range.

A segment is not just a label. It participates in the window boundary. If the active predicate stays true but the segment value changes, Spanfold closes the old active window and opens a new one with the new segment value. Tags do not do that; they describe the window without changing the interval shape.

Definitions

Choose the smallest concept that carries the meaning.

Partition

Isolates runtime state. It decides which state machine receives an event and prevents unrelated entities or lanes from mutating the same active window.

Segment

Splits windows when context changes. Use it for phases, lifecycle, market, deployment, or period boundaries that must be compared independently.

Tag

Describes a window without splitting it. Use it for fleet, customer, region, build number, or other metadata that should travel into exports and filters.

Example

Filter comparisons by the context that changes the answer.

Once segments and tags are recorded, they can be used in history queries, comparison scope, roll-up projection, and exports. Use segments when the answer should be measured inside a phase. Use tags when the same window should be discoverable by descriptive metadata.

var escalatedCritical = pipeline.History
    .Compare("Escalated critical fleet QA")
    .Target("primary", s => s.Source("primary"))
    .Against("backup", s => s.Source("backup"))
    .Within(scope => scope
        .Window("DeviceOffline")
        .Segment("lifecycle", "Incident")
        .Segment("stage", "Escalated")
        .Tag("fleet", "critical"))
    .Using(c => c.Overlap().Residual().Missing())
    .Run();