Skip to content

BecknTimeSeries v1.0: mandatory descriptors + @type rename#288

Merged
ameetdesh merged 1 commit into
mainfrom
feat/timeseries-descriptors-rename
Apr 29, 2026
Merged

BecknTimeSeries v1.0: mandatory descriptors + @type rename#288
ameetdesh merged 1 commit into
mainfrom
feat/timeseries-descriptors-rename

Conversation

@ameetdesh
Copy link
Copy Markdown
Collaborator

Summary

Three coordinated changes to BecknTimeSeries v1.0 and its only current consumer (DemandFlexPerformance v2.0):

  1. payloadDescriptors is now required on every BecknTimeSeries payload (was optional).
  2. Cross-field check — every intervals[*].payloads[*].type must be declared in payloadDescriptors[*].payloadType — added to the policy layer (Rego). It belongs schema-side too, but kin-openapi <0.136.0 silently drops the JSON Schema 2020-12 keywords needed to express it; this PR keeps the schema portable and lets Rego do the enforcement until the validator catches up. See kin-openapi PR #1125 for the upgrade path.
  3. JSON-LD cleanup — class IRIs move to a stable, unversioned deg: namespace; property IRIs stay versioned per-schema.

Why

payloadDescriptors were optional before, which made the embedded units / currency / reading-type sidecar easy to forget — and impossible to validate against. Making them required closes that gap.

The cross-field check ("descriptors cover every interval type") is the natural follow-on. It cannot be expressed in JSON Schema 2020-12 without enumerating every legal payloadType, and we deliberately keep that set open at the BecknTimeSeries layer (OpenADR's convention). So the check lives in Rego today; when kin-openapi 0.136.0+ ships, a consumer profile (e.g. DemandFlexPerformance) that closes its own enum can move the check schema-side.

The JSON-LD cleanup is incidental but worth doing now — the bts: prefix was self-referential and prevented other schemas from cleanly referring to the BecknTimeSeries class.

What changed

Schema — specification/schema/BecknTimeSeries/v1.0/

Before After
payloadDescriptors optional required, minItems: 1
@type enum ["BecknTimeSeries"] ["TimeSeries"]
payloadType set open string (OpenADR default) unchanged — open at this layer; consumer profiles close it

The schema name in components.schemas and the folder path stay BecknTimeSeries (versioned URLs are stable). Only the JSON-LD @type value used in payloads changes.

JSON-LD — namespace convention

DEG had a split convention across schemas. This PR picks one and applies it to the two schemas that move:

deg: https://schema.beckn.io/deg/                              (broad — class IRIs)
dfp: https://schema.beckn.io/deg/DemandFlexPerformance/v2.0/   (versioned — property IRIs)

Resulting class IRIs:

  • deg:BecknTimeSerieshttps://schema.beckn.io/deg/BecknTimeSeries
  • deg:DemandFlexPerformancehttps://schema.beckn.io/deg/DemandFlexPerformance

Other DEG schemas already on this convention (e.g. EnergyBillingSummaryCredential, EnergyMeterDataGB) needed no change. v2.0 schemas still using the old per-schema deg: self-namespace can migrate incrementally.

Policy — specification/policies/demand_flex_revenue.rego

New violation rule:

violations contains msg if {
    some i
    meter := _meters[i]
    declared_types := {d.payloadType | some d in meter.telemetry.payloadDescriptors}
    some interval in meter.telemetry.intervals
    some payload in interval.payloads
    not payload.type in declared_types
    msg := sprintf("meter %s: payload type '%s' used in intervals but not declared in payloadDescriptors", [meter.meterId, payload.type])
}

Examples — devkits/demand-flex/uc1-bdr-w-baselining/

  • on-status-response-baselines.json gained payloadDescriptors (was missing them; now required)
  • on-status-response-{actuals,settled}.json already had descriptors; only @type updated
  • Both Postman collections regenerated from the source examples — no hand-editing of escaped JSON

Sign convention (documented in README)

DEG follows OpenADR's "direction in the type name, magnitude positive" pattern — USAGE vs INJECTION, UP_REGULATION_CAPACITY vs DOWN_REGULATION_CAPACITY — rather than signed scalars. Reserved for DELTA_* types only. Future PayloadType additions should follow this convention.

Test plan

  • opa test specification/policies/demand_flex_revenue.rego specification/policies/demand_flex_revenue_test.rego — 8/8 pass
  • python3 devkits/demand-flex/scripts/generate_postman_collection.py --role BPP — clean regen
  • python3 devkits/demand-flex/scripts/generate_postman_collection.py --role BAP — clean regen
  • ./devkits/demand-flex/uc1-bdr-w-baselining/workflows/run-arazzo.sh against the running stack — will pass once merged to main, because the BPP fetches schemas from raw.githubusercontent.com/.../refs/heads/main/... at runtime. The three telemetry-bearing on-status steps NACK on the feature branch for that reason; non-telemetry steps (11/12) passed pre-merge.

Migration notes for downstream consumers

  • Any payload that already sets @type: "BecknTimeSeries" must update to @type: "TimeSeries". (The field is optional, so consumers that omit it are unaffected.)
  • Any payload missing payloadDescriptors now fails schema validation. Previously-shipped examples that did not include them need to be updated.

🤖 Generated with Claude Code

…eSeries, normalize JSON-LD namespaces

- payloadDescriptors is now required (minItems: 1) on BecknTimeSeries.
  payloadType stays open at this layer; consumer profiles close it and
  encode cross-field type-coverage as needed.

- @type discriminator: BecknTimeSeries -> TimeSeries. Schema name and
  folder path stay BecknTimeSeries (versioned URL); only the JSON-LD
  payload value changes.

- JSON-LD namespaces:
  * deg: https://schema.beckn.io/deg/      (broad, unversioned class IRIs)
  * dfp: https://schema.beckn.io/deg/DemandFlexPerformance/v2.0/  (versioned, schema-self property IRIs)
  Class IRI for the time-series envelope is deg:BecknTimeSeries
  (resolves to https://schema.beckn.io/deg/BecknTimeSeries) — a stable,
  unversioned reference that DemandFlexPerformance and any future
  consumer can point at without baking in v1.0.

- Cross-field check ("every type used in intervals[].payloads[].type
  appears in payloadDescriptors[].payloadType") added to
  demand_flex_revenue.rego. Lives in the policy layer until kin-openapi
  upgrades to v0.136.0+ and a profile-level if/then/else block can
  carry it schema-side.

- README rewritten: open-enum design, OpenADR sign-by-name convention
  (USAGE/INJECTION, UP_/DOWN_REGULATION_CAPACITY), pointer to consumer
  profiles for closure.

- demand-flex examples now include payloadDescriptors and @type=TimeSeries;
  postman collections regenerated from updated examples.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ameetdesh ameetdesh merged commit f9d07f9 into main Apr 29, 2026
@ameetdesh ameetdesh deleted the feat/timeseries-descriptors-rename branch April 29, 2026 14:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant