Caliper aggregate measures + membership status (closing CAL-VOCAB-2)
Status: accepted (increments ADR-0041; governed by ADR-0042)
CAL-VOCAB-2 is the last Caliper vocabulary requirement still todo that is buildable without the
held emission worker host. It binds three controlled vocabularies: an AggregateMeasure metric is a
Caliper metric term, an entity status is Active or Inactive, and a sensor declares the metric profiles
it supports as Caliper profile terms. CAL-VOCAB-1 already guards the Action vocabulary; this closes
the Metric / Status / Profile vocabularies.
The decision
Section titled “The decision”- AggregateMeasures are emitted as standalone entity describes, not events. No Caliper event type
accepts an
AggregateMeasureingenerated(e.g.ToolUseEventdefines no generated entity), and the spec models aggregate measures asAggregateMeasureentities in an Envelope’sdata[]. So the rail gains a thin entity-describe emission path: aCaliperWireDataItem = CaliperWireEvent | CaliperWireDescribeunion flows through the same outbox → Envelope → transport the events use (the worker already POSTsdata[]from generic outboxwirePayloads — only types widen). - The measures are de-identified package aggregates. Each describe is a COUNT across learners for
one external-package assignment (
UnitsCompleted,UnitsPassed), aggregated frompackage_progressGROUP BY assignment — carrying no person. So they are anonymous statistics: outside ADR-0042’s pseudonymous-actor erasure path entirely (nothing to sever), unlike the per-learner event rail. - Status rides the existing Membership.
statusis, in the Caliper 1.2 bootcamp model, a property only ofMembership— which we already emit (CAL-VOCAB-3). The reader mapsoffering_membership.membership_status→Active(active) /Inactive(any other), so the status vocabulary is honoured on a person-linked entity that the event rail already erases. - Done = the interop lane. CAL-VOCAB-2 flips when the conformance lane proves all three: an emitted
AggregateMeasure.metric∈ the Metric vocabulary, aMembership.status∈ {Active, Inactive}, and every emitted eventprofile∈ the Profile vocabulary (a drift guard like CAL-VOCAB-1’s for actions).
Considered alternatives
Section titled “Considered alternatives”- AggregateMeasure on a ToolUseEvent’s
generated. Rejected: the textual profile rule forbids any generated on ToolUseEvent; the spec carries aggregate measures as entity describes. - Per-learner AggregateMeasures. Rejected as the default: a single learner’s metric is personal
data needing the pseudonymous-actor erasure linkage, which an
AggregateMeasureentity has no slot for. De-identified package aggregates are both the truer meaning of “aggregate” and erasure-free. - A synthetic
statuson an invented entity. Rejected:Membershipis the spec’s status carrier and we already emit it — no new surface needed.
Consequences
Section titled “Consequences”- The outbox/Envelope/transport types widen to
CaliperWireDataItem; the snapshot of package aggregates is collected by a GROUP-BY query (collectPackageMetricsSnapshot) and enqueued as describes — built and idle until the held worker host runs it (the periodic metrics-snapshot push is part of the host’s job, exactly like the event rail is built-but-idle). - CAL-VOCAB-2 →
done; the Caliper board is 23 of 24. Only CAL-SND-1 remains, gated on the deployed-endpoint TLS smoke check (the held host). - Periodic-snapshot refinement (ADR-0049): a metric’s
metricValuechanges over time, but the outbox dedupes oneventId, so keying it on the measure IRI would emit each aggregate exactly once and freeze the value. The worker therefore enqueues each snapshot run under a run-scoped outbox identity (<iri>#<runId>) while keeping the wireAggregateMeasure.idthe stable metric IRI — the Endpoint receives asendTime-differentiated time series. OmittingrunIdkeeps the once-only IRI identity (the conformance lane’s single current-value describe).