ADR 0004 — Schema v1.1: Rolling-Window Aggregations (In-Place Enum Bump)
Status: Accepted
Date: 2026-04-27
Deciders: Josh Townsend (project lead)
Context
Phase 1.5 adds rolling-window aggregations: the ability for a rule condition to evaluate a statistic (avg, p95, etc.) over a sliding window rather than the full time series. This enables detection of transient spikes that full-series aggregation would mask.
The change requires a schema update. Two options were considered:
- New schema file:
dotnet/schemas/pal.pack.v1.1.jsonwith a new$id. - In-place enum bump: Edit
dotnet/schemas/pal.pack.v1.jsonto accept both"pal.pack/v1"and"pal.pack/v1.1"as validschema_versionvalues.
Decision: Option 2 — In-place enum bump
Rationale:
- The
windowfield is purely additive — it is optional onCondition, and v1 packs (withoutwindow) validate unchanged against the updated schema. There is no breaking change. - A new schema file would require updating all tooling that resolves
$idreferences, all pack-loading code that validates against a schema, and all documentation pointers. - The validator (
PackValidator) enforces the version-gate: a condition withwindowpresent must haveschema_version: "pal.pack/v1.1". This preserves the invariant that v1 packs are never exposed to v1.1-only fields. $schemaand$idcontinue to referencepal.pack/v1; theschema_versionenum is the operational discriminator, not the JSON Schema$id.
Schema changes to dotnet/schemas/pal.pack.v1.json
schema_version: changed fromconst: "pal.pack/v1"toenum: ["pal.pack/v1", "pal.pack/v1.1"].Condition.windowadded as an optional sub-object with fields:duration_seconds(required integer ≥ 30)step_seconds(optional integer ≥ 1)min_samples(optional integer ≥ 1, default 2)
Validator enforcement rules
When window is present on a Condition:
pack.schema_versionMUST be"pal.pack/v1.1"(error if not)aggregationMUST NOT be"trend"(windowed trend is undefined; error if set)step_secondsMUST be ≤duration_seconds(error if violated)
Consequences
- All existing v1 packs remain valid with no changes required.
- Authors wanting rolling-window rules bump
schema_version: "pal.pack/v1.1"and add awindow:block to conditions. RuleEvaluatordispatches toRollingWindowAggregatorwhencondition.Window != null.- The "worst window" value (max for gt/ge operators, min for lt/le) is reported as
ActualValue. - The expression string format for windowed conditions:
"p95(metric) over 5m rolling window > 90".
Not addressed here
- Wildcard metric aliases (referenced in Phase 1 spec §225)
- Schema v1.2+ changes