module
Data
The data engine. It aggregates raw telemetry into per-minute statistics, encodes those into the vectors the world model consumes, curates them, and publishes every dataset — training, validation, testing and runtime — through the same code. Size varies; the path does not.
The boundary
Data owns the canonical encoding surface: no module may define a competing encoding, and downstream modules load datasets rather than constructing producers. It sits below the model and control layers and imports none of them — enforced by tests/data/test_boundaries.py and tests/data/test_no_competing_encoding.py.
Decisions on record (18)
Inferred: decisions whose title names this module, or whose body references it as code. ADRs carry no declared statement of what they govern, so a decision that governs this module without naming it will be missing.
Public surface
MinuteMeanWindowaggregate.py · 5 public methods
as_table · clear · covers · extend · note_range
SignaliseDatasetdatasets.py · 3 public methods
feature_layout · manifest · split_indices
SequenceDatasetdatasets.py · 3 public methods
feature_layout · split_indices · window
FlowsheetCompilerflowsheet.py · 4 public methods
build_geojson · build_geojson_from_surface · from_environment · from_rows
FlowsheetSurfaceFileContractio.py · 2 public methods
read · write
TelemetryFileContractio.py · 2 public methods
read · write
RuntimeBundlemain.py · 3 public methods
actions · observations · rewards
DataModulemain.py · 21 public methods
begin_cycle · build_dataset_artifact · build_recent_steps · build_runtime_bundle · build_steps · canonical_step_interval_seconds · compile_flowsheet · export_flowsheet_surface · export_telemetry · feature_layout · from_environment · get_dataset · get_steps · import_flowsheet_surface · import_telemetry · layout_fingerprint_or_empty · list_datasets · maintain_datasets · maintenance_interval_seconds · node_operations · observe
WindowAnchorobservation.py · 1 public methods
measured
MembershipRowoperation_membership.py · 3 public methods
disagrees · unattributed · writable
MembershipReportoperation_membership.py · 4 public methods
disagreements · unattributed · verified · writable
DatasetQualityquality.py · 6 public methods
as_dict · concerns · excited · is_uniform · out_of_span · usable
StepStorestep_store.py · 2 public methods
build_steps · get_steps
Commands
signalise data build-dataset signalise data build-steps signalise data datasets signalise data feature-layout signalise data flowsheet signalise data inspect signalise data operations signalise data publish-datasets signalise data runtime-window signalise data steps signalise data telemetry
Enforced invariants (335)
The plant is reporting; one actuator's tag is not. That step is not complete.
test_a_dropped_action_tag_is_not_reported_as_a_complete_step
0.0 is a fabricated extreme in engineering units, not a neutral 'no reading'.
test_a_dropped_action_falls_back_to_its_nominal_not_to_zero
A closed valve reads 0.0. That is a measurement, not an absence.
test_a_genuine_zero_reading_is_kept_and_not_treated_as_missing
returns one step per minute
test_returns_one_step_per_minute
step ids are unique
test_step_ids_are_unique
step start iso strings
test_step_start_iso_strings
step end one minute after start
test_step_end_one_minute_after_start
plant id in every step
test_plant_id_in_every_step
flowsheet fingerprint consistent
test_flowsheet_fingerprint_consistent
encode_chunk v2.0.0 must not include an observation_layout key.
test_no_observation_layout_key
observation values is float list
test_observation_values_is_float_list
action values is float list
test_action_values_is_float_list
reward values is float list
test_reward_values_is_float_list
Spec 17: a constrained CV's dispersion is an ORDINARY observation dimension.
test_observation_values_is_base_dims_then_one_dispersion_dim_per_constrained_cv
`cv_ids` must be a subset of the observation vector — `std_index` indexes the PREDICTED
test_a_constrained_cv_is_an_observation_node_carrying_a_limit
missing count is int
test_missing_count_is_int
A minute with no telemetry is missing every node it needed, not just its observations.
test_missing_count_all_missing_when_no_telemetry
missing count zero when all present
test_missing_count_zero_when_all_present
Every node the step is built from counts — observations, actions AND rewards.
test_missing_count_partial
normalise ts trims to minute
test_normalise_ts_trims_to_minute
normalise ts distinct minutes
test_normalise_ts_distinct_minutes
Bulk (build) mode and runtime (serve) mode encode the same surface → identical fingerprint.
test_build_and_serve_produce_identical_fingerprint
Two independent Encoders over the same surface pin the same id orders (no ordering nondeterminism).
test_fingerprint_is_stable_across_encoder_instances
A different flowsheet must yield a different fingerprint (skew is detectable, not masked).
test_fingerprint_changes_when_flowsheet_changes
One write per node per backfill chunk. This is what churned the identity mid-run.
test_the_satellites_resume_cursor_is_not_the_flowsheet
a finished backfill is not the flowsheet
test_a_finished_backfill_is_not_the_flowsheet
A per-run historian URL and a controller mode say where data comes from, not how it encodes.
test_the_edges_coordinates_and_mode_are_not_the_flowsheet
touching a row is not changing it
test_touching_a_row_is_not_changing_it
a node becoming an action changes the flowsheet
test_a_node_becoming_an_action_changes_the_flowsheet
Amends ADR 0063, by ADR 0064: geometry is presentation, and the encoder cannot see it.
test_moving_equipment_does_not_change_the_ENCODING
The map's own freshness is a different question, and it keeps its own key — otherwise the
test_moving_equipment_DOES_change_the_drawing
Membership is a stored column since ADR 0062 and decides which operation a signal reaches.
test_reattributing_an_entity_changes_the_flowsheet
remapping a signal to another entity changes the flowsheet
test_remapping_a_signal_to_another_entity_changes_the_flowsheet
adding a node changes the flowsheet
test_adding_a_node_changes_the_flowsheet
the surface keeps every field the encoder reads
test_the_surface_keeps_every_field_the_encoder_reads
config hash is deterministic
test_config_hash_is_deterministic
config hash length
test_config_hash_length
config hash default is stable
test_config_hash_default_is_stable
encoder config defaults
test_encoder_config_defaults
`EncoderConfig` held exactly one thing: the colour ramp's clip (ADR 0064 deleted it).
test_the_config_carries_nothing_the_encoder_still_needs
nominal centered anchors
test_nominal_centered_anchors
clamps out of range
test_clamps_out_of_range
vector uses per index bounds and fallbacks
test_vector_uses_per_index_bounds_and_fallbacks
denormalize ∘ normalize == identity (within the bounds). Planner works in normalized space;
test_denormalize_is_inverse
The planner's setpoint recovery must be the shared function — no competing inverse.
test_planner_dispatch_uses_canonical_denormalize
The autopilot's runtime normalizer must be the same function — no competing encoding.
test_autopilot_runtime_delegates_to_canonical
material internal when both endpoints in one op
test_material_internal_when_both_endpoints_in_one_op
material transfer src to dst
test_material_transfer_src_to_dst
material plant input when src outside
test_material_plant_input_when_src_outside
material plant output when dst outside
test_material_plant_output_when_dst_outside
disjoint operations do not overlap
test_disjoint_operations_do_not_overlap
overlapping operations detected
test_overlapping_operations_detected
nested operations detected as overlap
test_nested_operations_detected_as_overlap
every reward is operation attributed
test_every_reward_is_operation_attributed
action features exist and are attributed
test_action_features_exist_and_are_attributed
operation of uses entity not node
test_operation_of_uses_entity_not_node
The decode block carries the STORED membership (ADR 0062) — the column, not the geometry.
test_operation_membership_block_reports_what_the_rows_say
The point of the column: absent membership is absent, not quietly filled in from geometry.
test_an_entity_with_no_stored_operation_attributes_to_nothing
D2: geometry proposes. Cleared columns change the record and leave the proposal untouched.
test_the_geometry_is_still_available_as_a_proposal
The property the column exists for: a polygon is a drawing, not a claim about the process.
test_moving_the_geometry_does_not_move_the_membership
The other half of D2: the proposal is the geometry's opinion, so it must track the drawing.
test_the_proposal_does_move_with_the_geometry
re identified rows produce the same ordered signals
test_re_identified_rows_produce_the_same_ordered_signals
The whole point: a clone or a restore must not invalidate a trained model.
test_the_fingerprint_survives_re_identification
The entity index is the primary sort key, and it used to come from raw DB row order.
test_entity_order_does_not_depend_on_row_order
node row order does not move a dimension
test_node_row_order_does_not_move_a_dimension
A tie on node name is broken by the controller, not by row identity.
test_mirrored_tag_names_stay_distinguishable
mirrored tag names order deterministically under row shuffle
test_mirrored_tag_names_order_deterministically_under_row_shuffle
Keyed on node name ALONE these two distinct surfaces hash identically — the controller half
test_the_fingerprint_separates_surfaces_that_share_a_node_name
Same tag names, different DCS attribution → a genuinely different surface.
test_swapping_the_controllers_changes_the_fingerprint
Same node name AND same controller: the key cannot identify the surface, so refuse rather
test_a_genuinely_ambiguous_surface_is_refused
This used to raise: a degree-scale polygon rastered to ~5e10 voxel cells at 0.5 m, so the
test_an_oversized_entity_is_no_longer_a_problem_to_guard
The guard must not fire on real plant geometry — an 11 m vessel is fine.
test_a_realistic_entity_still_encodes
the geometry does not propose an operation for the plant
test_the_geometry_does_not_propose_an_operation_for_the_plant
the plant keeps its signals
test_the_plant_keeps_its_signals
plant scope is read off the class not the name
test_plant_scope_is_read_off_the_class_not_the_name
the class comparison ignores case and padding
test_the_class_comparison_ignores_case_and_padding
`class DataEncoder` is defined exactly once, and that one definition is in signalise.data.encoder.
test_exactly_one_encoder_implementation_and_it_is_the_shared_library
The Data job (bulk mode) and the autopilot kernel (runtime mode) both import signalise.data.encoder —
test_both_a_job_and_the_kernel_import_the_shared_encoder
a healthy dataset has nothing to say
test_a_healthy_dataset_has_nothing_to_say
Not a warning. A constant action column carries no information about that action, so nothing
test_a_lever_that_never_moved_makes_the_dataset_unusable
a constant observation is named
test_a_constant_observation_is_named
The stockpile case: it varied — instrument noise guarantees that — and still said nothing,
test_a_channel_parked_against_a_limit_is_flagged_but_not_fatal
These land in a dataset's stored quality record, so an operator reading it later sees the
test_the_measures_survive_the_round_trip_into_a_record
an empty dataset is still unusable and says nothing false
test_an_empty_dataset_is_still_unusable_and_says_nothing_false
A transmitter cannot report past its own span — anything beyond it is a fault, not a value.
test_a_reading_outside_its_instruments_span_is_reported_as_a_fault
A PV tracking an SP overshoots. That is control, not a fault, and must not be flagged.
test_a_controller_overshooting_its_setpoint_range_is_not_an_instrument_fault
The declared graph is the plant's circuit.
test_a_material_wired_between_two_units_is_accepted
An edge to nowhere would leave a unit unreachable and silently unsimulated.
test_a_material_naming_equipment_that_is_not_there_is_refused
The pair plus the equipment they share is the loop.
test_a_setpoint_closing_on_a_measurement_is_accepted
A loop closes on a pv; anything else is a mis-declared loop, not a loop.
test_a_setpoint_closing_on_a_node_that_is_not_a_measurement_is_refused
A loop that commands one unit and measures another is a description error.
test_a_setpoint_closing_on_another_units_measurement_is_refused
Presence is not required yet — Telfer's circuit is not described. Consistency is.
test_a_flowsheet_that_declares_neither_is_still_accepted
ADR 0097 D2 — aggregation is derived from the quantity, so the quantity must say.
test_every_unit_of_measure_says_whether_it_is_extensive
a resolvable source is accepted
test_a_resolvable_source_is_accepted
a source that names something absent is refused
test_a_source_that_names_something_absent_is_refused
An operator fixing a 191-node flowsheet needs to know which node and which reference.
test_the_refusal_names_the_node_and_what_it_could_not_find
a continuously reported reward gets a dispersion channel
test_a_continuously_reported_reward_gets_a_dispersion_channel
A 12-minute analyser cannot vary inside a 1-minute step, so its spread is always zero.
test_a_reward_reported_slower_than_a_step_gets_none
Faster than the step still varies within it; at or slower than the step, it cannot.
test_the_boundary_is_the_step_interval
A DCS block recomputes every scan, but it cannot report news its inputs have not delivered.
test_a_computed_tag_inherits_the_cadence_of_its_slowest_input
The loader reconstructs the model's input from the artifact alone — no database, no encoder.
test_a_published_artifact_loads_into_the_model_input
Splits are chronological ranges over one contiguous encode — disjoint, and covering the set.
test_the_splits_partition_the_set_without_overlap
The correctness reason for per-split artifacts, not merely a tidiness one.
test_a_per_split_artifact_cannot_leak_across_the_boundary
Published has to mean LOADABLE. The first live publish put arrays and a manifest in the bucket
test_a_split_artifact_is_self_contained
A window is a sample; a sample containing rows from two splits belongs to neither.
test_a_window_belongs_to_one_split_only
The load-bearing property: no dropped step is counted twice, and none disappears.
test_the_shares_sum_to_everything_that_was_dropped
a dropped step lands in the split whose range contains it
test_a_dropped_step_lands_in_the_split_whose_range_contains_it
Steps outside the surviving span still belong to a split — the first or the last.
test_the_edges_are_absorbed_not_lost
a clean window reports zero
test_a_clean_window_reports_zero
time weighted mean and dispersion
test_time_weighted_mean_and_dispersion
causal rolling zscore
test_causal_rolling_zscore
deferred identity resolution and orphan skip
test_deferred_identity_resolution_and_orphan_skip
inactive node excluded
test_inactive_node_excluded
gap minutes produce no row
test_gap_minutes_produce_no_row
window bounds half open
test_window_bounds_half_open
empty raw returns empty
test_empty_raw_returns_empty
incremental and full derivation produce identical rows
test_incremental_and_full_derivation_produce_identical_rows
`build_recent_steps` resumes from the newest built step, so consecutive builds overlap by the
test_overlapping_builds_still_use_the_window
Coverage keys on the RANGE aggregated, not on calendar minutes — an empty minute is not a
test_a_skipped_range_makes_the_next_build_re_derive
a cold window matches the full derivation
test_a_cold_window_matches_the_full_derivation
Bounded by TIME from the newest minute — trimming by count would evict minutes still inside
test_the_window_is_bounded_by_its_capacity
a null saturation hashes as if the column were not there
test_a_null_saturation_hashes_as_if_the_column_were_not_there
It changes what the planner optimises, so it is a different plant description.
test_declaring_a_saturation_point_does_move_the_fingerprint
two different knees are two different flowsheets
test_two_different_knees_are_two_different_flowsheets
data encoding surface imports no model or control module
test_data_encoding_surface_imports_no_model_or_control_module
The derivative must not be consulted about the source it is derived from.
test_bounds_never_ask_the_database
A label nothing reads, on an answer nothing should trust.
test_there_is_no_database_source_label
`unavailable` and `no_telemetry` must stay separate — only one of them is a fault.
test_an_unreadable_raw_store_is_distinct_from_a_cold_plant
The trainer's 90-day request against 28 days of telemetry.
test_a_window_wider_than_coverage_is_clamped_to_it
Clamping must never NARROW a legitimate request.
test_a_window_inside_coverage_is_left_alone
only the leading span is trimmed when the end is inside
test_only_the_leading_span_is_trimmed_when_the_end_is_inside
A plant with no partitions yet must not be silently clamped to nothing.
test_no_raw_coverage_leaves_the_request_untouched
Ordering is the point: the clamp must precede the chunk loop, not correct it afterwards.
test_build_steps_reads_the_bounds_before_walking_chunks
a node dynamics map is rekeyed onto the clones nodes
test_a_node_dynamics_map_is_rekeyed_onto_the_clones_nodes
The ids are already the target's, so there is nothing to remap. Rewriting them would be the
test_a_same_plant_carry_is_left_alone
A node the clone genuinely lacks must stay recognisable as unmatched, not be rewritten into a
test_an_id_with_no_counterpart_on_the_target_is_not_invented
Spec 17's dispersion channels are keyed `<node id>:std` and are what the excursion gate
test_a_derived_channel_key_carries_its_node_id_across
import flowsheet returns plant id
test_import_flowsheet_returns_plant_id
import flowsheet table counts
test_import_flowsheet_table_counts
compile flowsheet after import
test_compile_flowsheet_after_import
loaded telemetry covers window
test_loaded_telemetry_covers_window
import telemetry plant id
test_import_telemetry_plant_id
All 10080 steps are either written or skipped (idempotent total).
test_build_steps_all_steps_covered
build steps no missing
test_build_steps_no_missing
Re-running build_steps on already-current steps skips all of them.
test_build_steps_idempotent
get steps returns 10080
test_get_steps_returns_10080
step has required fields
test_step_has_required_fields
step plant ids correct
test_step_plant_ids_correct
encoder config hash consistent
test_encoder_config_hash_consistent
flowsheet fingerprint consistent
test_flowsheet_fingerprint_consistent
episode ids contain plant id
test_episode_ids_contain_plant_id
10 080 steps (7 days) at 360 steps/episode = 28 distinct episode IDs.
test_steps_span_multiple_episodes
build dataset returns id
test_build_dataset_returns_id
build dataset manifest fields
test_build_dataset_manifest_fields
build dataset split counts sum to steps
test_build_dataset_split_counts_sum_to_steps
build dataset split fractions
test_build_dataset_split_fractions
build dataset feature layout
test_build_dataset_feature_layout
build dataset fingerprint set
test_build_dataset_fingerprint_set
Second call returns the same dataset_id (fingerprint collision → same row).
test_build_dataset_idempotent
materialise returns artifact dir
test_materialise_returns_artifact_dir
materialise cache files present
test_materialise_cache_files_present
materialise array shapes
test_materialise_array_shapes
Observation values are signed intensities in [-1, 1].
test_materialise_obs_values_signed_intensity
materialise step meta length
test_materialise_step_meta_length
materialise signalise dataset loads
test_materialise_signalise_dataset_loads
materialise split indices cover all steps
test_materialise_split_indices_cover_all_steps
materialise dataloader batch shape
test_materialise_dataloader_batch_shape
train loader length
test_train_loader_length
val loader length
test_val_loader_length
test loader length
test_test_loader_length
all loader length
test_all_loader_length
train loader shuffle
test_train_loader_shuffle
val loader not shuffled
test_val_loader_not_shuffled
batch keys
test_batch_keys
batch shapes
test_batch_shapes
invalid split raises
test_invalid_split_raises
loads from cache
test_loads_from_cache
getitem keys
test_getitem_keys
getitem tensor shapes
test_getitem_tensor_shapes
getitem step meta
test_getitem_step_meta
getitem out of range
test_getitem_out_of_range
split indices train
test_split_indices_train
split indices val
test_split_indices_val
split indices validation alias
test_split_indices_validation_alias
split indices test
test_split_indices_test
split indices all
test_split_indices_all
split indices invalid
test_split_indices_invalid
feature layout node
test_feature_layout_node
manifest node
test_manifest_node
A missing artifact is an error, not a trigger to build one (ADR 0069 D3).
test_raises_when_the_artifact_is_not_there
is cached false when missing
test_is_cached_false_when_missing
the dispersion channels reach the array
test_the_dispersion_channels_reach_the_array
Steps encoded before the dispersion channels existed. Coercing them is how a 67-wide dataset
test_a_step_that_does_not_match_the_layout_is_refused_not_truncated
No CV carries a limit -> no dispersion block -> the vector is exactly the observation nodes.
test_a_plant_with_no_cvs_is_unaffected
incomplete steps are excluded
test_incomplete_steps_are_excluded
A dataset quietly losing rows is its own defect.
test_the_exclusion_count_is_returned_not_swallowed
the exclusion is logged
test_the_exclusion_is_logged
a fully complete window excludes nothing and stays quiet
test_a_fully_complete_window_excludes_nothing_and_stays_quiet
`_build_dataset` sizes the splits, `_materialise_dataset` writes the rows. A filter on one and
test_both_dataset_paths_use_the_shared_selector
an all incomplete window fails loudly rather than building nothing
test_an_all_incomplete_window_fails_loudly_rather_than_building_nothing
One day imported over a month of raw: refuse, and name what it would have cost.
test_refuses_to_purge_history_it_is_not_replacing
A clean-room arm means it — and now has to say so.
test_the_declared_exemption_lets_a_benchmark_arm_through
The throwaway benchmark plant: nothing to protect, so nothing to refuse.
test_a_fresh_plant_is_not_blocked
every constrained cv gets a contiguous dispersion dimension
test_every_constrained_cv_gets_a_contiguous_dispersion_dimension
The whole point of issues/0026: `_constrained_cvs` must find a real index, not None.
test_std_index_reaches_the_gate
Regression that was live for exactly one commit while writing this.
test_the_dispersion_block_moves_the_fingerprint
a flowsheet encodes to a step
test_a_flowsheet_encodes_to_a_step
The specific disagreement that was possible: the geojson publishes the order every consumer
test_the_published_layout_is_the_order_the_encoder_used
the signal layout and the encoder agree
test_the_signal_layout_and_the_encoder_agree
ADR 0064 deleted it. A lattice nobody reads is worth catching if it returns, because the last
test_the_geojson_no_longer_publishes_a_lattice
every entity id is remapped and scoped to the target
test_every_entity_id_is_remapped_and_scoped_to_the_target
references follow their targets
test_references_follow_their_targets
The walk replaces any string that IS a mapped id, so a reference this function has never
test_ids_nested_anywhere_are_rewritten_too
the mapping is deterministic so reimport is idempotent
test_the_mapping_is_deterministic_so_reimport_is_idempotent
a different target gets different ids
test_a_different_target_gets_different_ids
source plant users do not come along
test_source_plant_users_do_not_come_along
counts match per entity type
test_counts_match_per_entity_type
name sets match per entity type
test_name_sets_match_per_entity_type
signals compile to nodes
test_signals_compile_to_nodes
signal fks and semantics resolve
test_signal_fks_and_semantics_resolve
ids are minted uuids not source ids
test_ids_are_minted_uuids_not_source_ids
geometry roundtrips via cell
test_geometry_roundtrips_via_cell
blank template has null geometry
test_blank_template_has_null_geometry
validation collects pk and fk errors
test_validation_collects_pk_and_fk_errors
get steps pages past the 1000 cap
test_get_steps_pages_past_the_1000_cap
get steps exact multiple of cap
test_get_steps_exact_multiple_of_cap
get steps respects limit
test_get_steps_respects_limit
get steps empty
test_get_steps_empty
flowsheet read plain json
test_flowsheet_read_plain_json
Regression: .gz flowsheets must read without NameError (gzip import).
test_flowsheet_read_gzip_transparent
A document with a singular `plant` object is normalised into `plants`.
test_flowsheet_plant_singular_fallback
Export writes the single customer format: controller_name,node_id,value,source_timestamp.
test_telemetry_write_is_controller_node_keyed_csv
no module outside data computes the action encoding
test_no_module_outside_data_computes_the_action_encoding
The pair is kept together so it cannot drift; this is what 'cannot drift' has to mean.
test_the_canonical_map_and_its_inverse_round_trip
A refused window must not silently answer 'no layout change' — that disables the gates.
test_the_fingerprint_does_not_depend_on_the_step_window
an unreadable flowsheet is the only unknown
test_an_unreadable_flowsheet_is_the_only_unknown
180 steps is an invariant, not a default — a settings value must not undercut it.
test_the_window_is_floored_at_the_runtime_minimum
a larger window is honoured
test_a_larger_window_is_honoured
the window spans the requested minutes
test_the_window_spans_the_requested_minutes
The signature IS the contract. This took a `RuntimeSnapshot` and reached into it for another
test_it_takes_the_values_it_needs_not_the_kernel_snapshot
a dense window has no lag
test_a_dense_window_has_no_lag
Continuity, not wallclock recency — so one limit works for live AND compressed replay.
test_a_gappy_window_reports_the_gap_as_seconds
A satellite stopped six hours ago yields a window that slid back with it: still dense, lag 0.
test_wallclock_age_sees_a_dead_stream_that_continuity_cannot
wallclock age is never negative
test_wallclock_age_is_never_negative
no action targets is a mapping failure in autopilot
test_no_action_targets_is_a_mapping_failure_in_autopilot
Shadow writes no telecommand, so an unmapped action surface is not yet a defect.
test_no_action_targets_is_tolerated_in_shadow
a readable bound anchors the window to the plant
test_a_readable_bound_anchors_the_window_to_the_plant
a cold plant anchors to now and says so
test_a_cold_plant_anchors_to_now_and_says_so
The measured failure: the DB answered (None, None) while raw held data eight hours newer.
test_an_unreadable_bound_is_not_silently_treated_as_a_cold_plant
a raising bounds call is loud and unmeasured
test_a_raising_bounds_call_is_loud_and_unmeasured
an unparseable bound does not become wallclock
test_an_unparseable_bound_does_not_become_wallclock
`now` makes the age zero by construction — the most reassuring number, and meaningless.
test_freshness_is_withheld_when_the_anchor_was_assumed
freshness is reported when the anchor was measured
test_freshness_is_reported_when_the_anchor_was_measured
a fully sensed window says so
test_a_fully_sensed_window_says_so
A signal with no reading encodes to its declared NOMINAL, nominal standardises to z ~= 0, and
test_a_window_the_plant_never_measured_is_named_not_hidden
Normal plant life. The runtime has ~180 steps against the dataset's 43,000 and must act, so
test_one_sensor_dropping_out_is_partial_not_blind
an empty window is not reported as sensed
test_an_empty_window_is_not_reported_as_sensed
Downstream modules load datasets; they do not build the thing that makes them.
test_no_module_outside_data_constructs_the_producer
Training, validation, testing and runtime carry the same verdict fields, because 'the quality
test_every_split_is_rated_by_the_same_call
More than one encoder identity in a set means mixed vector orders — the ADR 0042 failure, at
test_a_mixed_encoding_identity_is_not_usable
Every value filled from nominal is a record of nothing — what the dataset builder already
test_a_fully_imputed_set_is_not_usable
an empty set is rated rather than crashing
test_an_empty_set_is_rated_rather_than_crashing
writes only what is missing
test_writes_only_what_is_missing
Geometry proposes; it does not override (D2). A disagreement is reported, not resolved.
test_a_stored_membership_is_never_overwritten
The correct answer, and the one that was previously invisible.
test_an_entity_the_geometry_places_nowhere_stays_null
verified only when every proposed membership is stored
test_verified_only_when_every_proposed_membership_is_stored
materials are written through their own rpc
test_materials_are_written_through_their_own_rpc
the backfill is idempotent
test_the_backfill_is_idempotent
rows without an id are skipped not written
test_rows_without_an_id_are_skipped_not_written
writable / disagreements / unattributed partition the rows that are not already correct.
test_report_properties_are_disjoint
the flowsheet is compiled once per cycle
test_the_flowsheet_is_compiled_once_per_cycle
a second plant gets its own surface
test_a_second_plant_gets_its_own_surface
the runtime bundle is built once per window
test_the_runtime_bundle_is_built_once_per_window
a different window is a different bundle
test_a_different_window_is_a_different_bundle
The memo must not outlive the cycle — a new module is a new cycle.
test_a_fresh_module_starts_cold
`source_timestamp` is a ROW-level predicate — pyarrow opens every file's metadata to evaluate
test_read_window_prunes_on_the_day_partition
The live drain writes one small object per poll; a day accrues thousands of ~3 KB files.
test_compaction_merges_drain_objects_and_preserves_every_row
Backfill files are already well-sized and are idempotently rewritten by replay — folding them
test_compaction_leaves_the_backfill_objects_alone
Nothing to merge must not rewrite the partition — a rewrite-for-nothing is pure risk.
test_compaction_is_a_no_op_below_two_objects
Merging in arbitrary order destroys timestamp locality, and a reader then cannot prune:
test_compaction_sorts_by_time_so_readers_can_prune
`source_timestamp` is a ROW-level predicate — pyarrow opens every file's metadata to evaluate
test_read_window_prunes_on_the_day_partition
a changed layout changes the step encoding identity
test_a_changed_layout_changes_the_step_encoding_identity
the same layout is stable
test_the_same_layout_is_stable
A caller that forgets the layout gets a DIFFERENT hash from one that supplies it — so the
test_the_layout_is_not_silently_optional_for_the_step_store
Regression on the shape of the wiring: an earlier version read the fingerprint off the
test_the_step_store_actually_receives_it
a step from the same flowsheet and encoder is current
test_a_step_from_the_same_flowsheet_and_encoder_is_current
The regression: same encoder hash, older flowsheet — it must be re-encoded, not skipped.
test_a_step_from_a_DIFFERENT_flowsheet_is_not_current
a step from a different encoder is still not current
test_a_step_from_a_different_encoder_is_still_not_current
a split window keeps only the steps that match both
test_a_split_window_keeps_only_the_steps_that_match_both
a row with no fingerprint is re encoded rather than assumed to match
test_a_row_with_no_fingerprint_is_re_encoded_rather_than_assumed_to_match
no fingerprint asked for falls back to the encoder hash alone
test_no_fingerprint_asked_for_falls_back_to_the_encoder_hash_alone
raises when steps written but zero telemetry
test_raises_when_steps_written_but_zero_telemetry
ok when no steps written
test_ok_when_no_steps_written
ok with full coverage
test_ok_with_full_coverage
warns but passes on low coverage
test_warns_but_passes_on_low_coverage
each protocol lands in its own prefix
test_each_protocol_lands_in_its_own_prefix
a window is read from both
test_a_window_is_read_from_both
ADR 0078 D2. The operation the ADR exists for: PI recalculated n1, and OPC's record of n2 is
test_a_resync_replaces_the_historian_and_leaves_opc_alone
ADR 0078 D5. A resync exists because the source produced a BETTER number; deferring to the
test_where_both_cover_a_minute_the_historian_wins
opc survives where the historian says nothing
test_opc_survives_where_the_historian_says_nothing
No `telemetry/pi` prefix exists at all — an absent source is not an error.
test_a_plant_that_never_synced_a_historian_reads_fine
A plant provisioned earlier has rows directly under `telemetry/`, with no subfolder. They are
test_telemetry_written_before_this_adr_is_still_read
The bug this nearly shipped with: reading the legacy root recurses INTO `telemetry/opc`, so
test_a_row_is_never_read_twice
the row count is exact across both protocols
test_the_row_count_is_exact_across_both_protocols
After build_steps, telemetry serves a per-node series over the built window —
test_build_steps_publishes_the_minute_tier
A single-node query returns the get_telemetry-compatible stats object the console reads:
test_the_minute_tier_single_node_contract
The rolling window stats use the SAME causal window functions as the retired get_telemetry —
test_the_minute_tier_causal_window_stats
The hand-written `__all__` and the map that resolves it cannot disagree.
test_all_agrees_with_the_export_map
Deferred resolution must not defer a failure: every advertised name really exists.
test_every_exported_name_resolves
`__getattr__` must not turn a typo into something other than AttributeError.
test_an_unknown_name_raises_attribute_error
The reason the surface is lazy at all — assert the benefit, not just the mechanism.
test_the_pure_function_does_not_drag_torch_in
It materialised on load when the cache missed — a LOADER creating the thing it loads. No
test_the_loader_takes_no_download_flag
the loader cannot reach the engine
test_the_loader_cannot_reach_the_engine
It called get_settings(scope="global", module="data"), which returns the VOCABULARY
test_from_environment_resolves_the_modules_own_settings
no module outside the engine builds a dataset
test_no_module_outside_the_engine_builds_a_dataset
`maintain_datasets` is the sequence. If it disappears, the spray comes back.
test_the_engine_exposes_one_maintenance_entry_point
A shipped flowsheet is the worked example; it must pass the gate it demonstrates.
test_a_shipped_flowsheet_declares_only_options_the_vocabulary_carries
A class exists because a plant can be described with it, not because it was listed.
test_every_simulated_equipment_class_is_reachable_from_a_flowsheet
ADR 0094 D4 — a unit carries its UCUM code, or states that it is not a physical unit.
test_units_declare_a_ucum_code_or_say_why_not
The unit must build over a bounded trailing window (step_build_window_days), NOT windowless
test_per_cycle_build_is_bounded_not_windowless
per cycle window falls back to now without bounds
test_per_cycle_window_falls_back_to_now_without_bounds
A telemetry-bounds lookup error must not abort the build — it falls back to `now`.
test_step_build_is_best_effort_on_bounds_error
step build window days default
test_step_build_window_days_default
step build window days from settings
test_step_build_window_days_from_settings
A bounded window still re-aggregated all 1,440 minutes of it every cycle to append one step.
test_build_resumes_from_the_newest_existing_step
No steps yet → there is nothing to resume from, so the full window is the correct work.
test_a_cold_plant_still_builds_the_whole_window
Resume is an optimisation — if the lookup fails the build must still run, over the full
test_resume_survives_a_get_steps_failure
A changed feature layout must rebuild the WHOLE window, not resume past the stale steps.
test_resume_does_not_skip_past_steps_encoded_under_a_different_layout
the cycle compacts the day it is about to encode
test_the_cycle_compacts_the_day_it_is_about_to_encode
compaction is bounded by its settings
test_compaction_is_bounded_by_its_settings
zero min objects stops compaction entirely
test_zero_min_objects_stops_compaction_entirely
a failed compaction does not stop the encode
test_a_failed_compaction_does_not_stop_the_encode
A backfill lands BEHIND the recent window, and the recent window never travels back to it.
test_raw_older_than_any_step_is_encoded
It walks back to where the history actually starts, not past it into empty time.
test_the_catch_up_stops_at_the_oldest_raw
Converges: when every raw sample is behind an encoded step, the extra build stops.
test_no_catch_up_once_the_backfill_is_covered
Sources
Generated from the repository. Every section is derived from an artifact the build enforces — ADR frontmatter, the architecture tests, the module's AST, its test docstrings, and the CLI's own help. Nothing here is hand-written prose, so nothing here can drift from the code without the code changing first.