Regulator profiles
A framework written so that a program can run it: what a profile holds, the rules it meets, how a flow runs and how a trace is checked.
The first profile is AER Directive 038. The rules and the evaluator can still change before the first release.
A regulator profile is a framework written so that a program can run it. It states how to judge a development against one regulation: the limits, the periods, the criteria, the controls and the order of the checks. Anyone can read it, run it and compare its result with a report.
A profile is an ODS document. It needs no schema of its own
beyond the RegulatorProfile record, so every ODS reader can open it. The
package @opencycle/ods-profiles holds the published profiles and the open evaluator
that this page describes.
1. What a profile holds
| Record | Where | What it states |
|---|---|---|
| RegulatorProfile | risk.profiles |
The profile: its publisher, its version, its status, the framework it states, its flows, its scope and the readings it chose. |
| Framework | risk.frameworks |
The regulation: issuer, jurisdiction, edition, the dates it is in force and its citation. |
| Period | the framework | Each window that repeats, such as night-time. |
| Variable | the framework | Each limit and constant of the regulation, with its unit. |
| Policy and Criterion | the framework | Each rule that is judged: what is compared, how, and with which limit. |
| Control | the framework | Each check on how the evaluation is done, such as an approval or the class of a meter. |
| Flow | methodology.flows |
The order of the checks. One flow for each purpose, such as an assessment before construction and the investigation of a complaint. |
| Assumption | methodology.assumptions |
Each reading the publisher chose where the regulation leaves a choice open. |
| Stakeholder and Jurisdiction | context |
The issuer, the publisher and where the framework applies. |
A document that reports on a development holds a copy of the profile it was judged by, with the development, the evidence and the traces. A reader then needs no second file to check the result.
The publisher is not the regulator, unless the regulator publishes the profile. A profile states its publisher and says in its description whether the regulator wrote or endorsed it. The regulation is the authority.
2. Rules for a profile
- Every rule is open. A profile's flows have no sealed steps. A calculation method can be closed; the rules that judge a development are public. A profile takes a sound level, a concentration or another result as an answer, and its controls say which methods and evidence that answer needs.
- Every rule is cited. Each criterion, control and variable names the part of the framework it comes from: a section, a table or a numbered requirement.
- Every number is a Variable. A limit or a constant of the regulation is a Variable with a unit and a citation. Expressions refer to it by id, so a reader can find each number once.
- Every choice is stated. Where the regulation is silent or says two
things, the profile takes one reading and records it as an Assumption
whose
subjectis the step, criterion or variable it changes. The profile lists them inassumptions. - A decision names its criterion. A step that judges a criterion
holds its id in
criterion. A step that records a control holds its id incontrol. - The scope says what is left out. The profile's
scopenames the parts of the regulation it does not state.
3. The profile check
checkProfile(document) checks each profile of a valid document. Each
failed rule is a Finding with level error, origin check
and the record's place as a JSON Pointer in data.path. A profile's flows
are the flows it names and each flow that one of their steps runs.
| Code | Rule |
|---|---|
profile.missing |
The document has a profile in risk.profiles, and the profile names at least one flow that the document holds. When it names none, the other rules are not checked. |
profile.sealed_step |
No step of the profile's flows is sealed. |
profile.citation_missing |
Each criterion, control and variable of the profile's framework has a citation with a section. |
profile.variable_unresolved |
Each variable in an expression is the id of a Variable of the framework or of the profile's flows, or the name of an output of a step of those flows. This applies to a step's expression, an edge's when, a criterion's limit and when, and a variable's expression. |
profile.operation_unsupported |
Each operation in those expressions is one the open evaluator runs (section 4). |
profile.entrypoint |
Each flow has exactly one entry point. |
profile.step_unreachable |
A path of edges from the entry point reaches each step. |
profile.no_outcome |
Each step that is not an outcome has an edge, so a flow never ends without an outcome. |
4. How a flow runs
A run starts with answers: named amounts that the caller gives. It keeps every named amount in one set, which the steps read and write. A flow that runs as a step of another flow shares that set.
Steps. The run starts at the entry point.
| Step type | What the run does |
|---|---|
entrypoint, outcome |
Nothing. An outcome ends its flow. |
calculation |
Works out its expression and writes the amount to its first output. |
decision |
With a criterion: compares its first input with the criterion's limit, as the criterion's comparison says, and writes true or false to its first output. The run records the judgement. When the first input is a list, each item is the amount of one subject and is one judgement, against the limit or, when the limit is a list of the same length, against the limit at the same place; an item with no data is not judged, and the step writes true when every judgement is met. Without a criterion: nothing; its edges choose. |
flow |
Runs the flow that flow names, then writes the id of the outcome that flow ended at to its first output. |
| every other type | Takes each of its outputs from the answers, by the output's name. |
A program cannot work out a question, a context step, a control step or an algorithm step, so their outputs are answers. An output with no answer is no data. A required question with no answer blocks the run.
No data is never 0 and never false. A calculation that needs an amount
that is no data fails, and the run fails with the step named. A flow tests
for no data on purpose: {"operation": "equal", "arguments": [{"variable": "ambient_night"}, null]}.
Edges. After a step, the run tests the step's edges: higher priority
first, then the order written. It takes the first edge that has no when,
or whose when is true. A when that is not true or false fails the run.
When no edge holds, the run is blocked. A step with no edge ends its flow
without an outcome. Each way out of a step is an edge, also the one a
decision takes when nothing else holds: a producer whose own flows leave
that default unwritten writes it as an edge with no when, so a trace can
name the edge of every branch.
Expressions. A variable is read from the run's named amounts first,
then from the Variables by id. A value gives its amount. Units are not
converted: a profile keeps one unit for each quantity. The evaluator runs
these operations:
| Operations | Notes |
|---|---|
and, or, not, if |
The arguments must be true or false. and and or stop at the argument that decides them. if works out only the branch it takes. |
equal, not_equal |
Plain amounts, no data included. Amounts of different types are never equal. |
less, less_or_equal, greater, greater_or_equal |
Numbers. |
in |
The second argument is a list. |
add, subtract, multiply, divide, negate, power, log10 |
Numbers. |
round |
Half away from zero, to the number of decimals that is the second argument. |
minimum, maximum, sum, mean, count |
Numbers. A list argument counts as its items. |
energy_sum, energy_mean |
Levels in decibels: 10 log10 of the sum, or the mean, of 10^(L/10). A list argument counts as its items. |
It does not run extract, map, filter, reduce, distance,
intersects, within, buffer or custom. A profile does not use them
(profile.operation_unsupported).
The result. run(document, flow, answers) gives the status
(completed, blocked or failed), the outcome, every named amount, the
steps in the form of a Trace's steps, and each judgement: the criterion,
the amount judged, the limit and whether the criterion is met.
One receptor for each run. A flow judges one receptor. A caller runs it
once for each receptor that the regulation names, such as the nearest
dwelling and each dwelling that may be more affected.
A producer's flow may judge several receptors in one run: its decision
then judges a list, with one amount for each receptor, and the run records
one judgement for each, with the receptor's place in the list (subject).
5. The trace check
replay(document, trace) runs the trace's flow again with the outputs the
trace recorded as answers, and compares the two. The run takes an answer
only for a step it cannot work out, so a recorded result never stands in
for a calculation.
The new run starts with the values the trace says the run was given
(scenarioValues), each as a named amount under its key. A step that
writes the same name replaces it.
A step value that holds a list whole (data) and no amount gives that
list as the answer, so a condition on the list (its count) can be
worked out. A step value with
no amount and no list, which names the records it stands for (records),
gives those records as its list.
A step takes the answer the trace recorded for that step, at that visit: two steps can write one name, and a step in a loop writes it again.
A producer can keep the run of a sub-flow as a trace of its own. The
flow step of the parent's trace then names that trace in traces. The
check does not run such a sub-flow in line: the step keeps the outputs
the trace recorded, and the check of the child's trace proves the
sub-flow. Without traces, the sub-flow runs in line and its steps
follow the flow step in the parent's trace.
A step value can name where it came from (sources), when another trace
of the document wrote it: one item for each item of the value's list, or
one item for one amount. An item names the trace and the name that trace
wrote the value under; null names no source. The check compares each item
with the last value that trace wrote under that name. A limit that one run
worked out and another run judged against is then one value, not two. The
check reads the inputs and the outputs of every step of the trace.
| Code | Rule |
|---|---|
trace.missing |
The document holds the trace, and the trace names a flow that the document holds. The document holds each trace that a value's sources name. |
trace.mismatch |
A new run gives the trace's status, the same steps in the same order, the same outputs and units for each step (by name, in any order), the same edge for each decision, and the trace's verdict. A value has one source for each item, and each item with a source is the last value its source wrote under that name. |
The worst result decides the verdict of a run. It is pass when every
criterion it judged is met, fail when at least one is not met, and none
when it judged nothing. partial means that no criterion failed and at
least one is met only in part, such as with a warning or a condition. The
evaluator judges each criterion as met or not met, so a run it works out is
never partial; a producer that states partial says why in the verdict's
basis. The trace check takes a trace's partial as agreeing with a new
run that judges at least one criterion and fails none. The same rule gives the level of each subject, and the run's level
is the worst level of its subjects that have one. A subject that was not
judged has no level, and its reason says why. The validator checks the
run's level against its subjects (verdict.level,
validation).
After the first step where the two runs are at different steps, later
steps are not compared.
The trace check proves that the worked-out steps follow from the answers. It does not prove that an answer is true: the evidence and the controls do that.
6. Published profiles
| Profile | Framework | Version | File |
|---|---|---|---|
ca.ab.aer.directive_038 |
Alberta Energy Regulator, Directive 038: Noise Control (April 2024) | 0.1.0, draft | @opencycle/ods-profiles, profiles/ca-ab-aer-directive-038.json |
The Directive 038 profile works out the permissible sound level (the basic sound level of table 1, the Class A and Class B adjustments), judges a noise impact assessment and judges a complaint from a comprehensive sound level survey, with the low frequency noise penalty. It states each of the directive's 58 numbered requirements as a criterion, a control or a step. Its tests take every expected number from the directive's tables, its figure 1 and its worked examples. The Alberta Energy Regulator did not write it and has not endorsed it.
7. Not in this version
- A run for several receptors at once, and operations on a document's
records and shapes (
extract,map,distanceand the others). - A hash that ties the copy of a profile in a document to the published profile.
- A check that the evidence behind an answer meets a control, such as the class of the meter that measured a level.
- Tables as Values with axes; a profile writes a table as Variables and
ifoperations.