# October 14, 2026 (round 7)
Robust and consistent behavior for every public function.
fix: wrong results: CrossSignalAnalytics.lead_lag named the wrong leader;
  PatternRecognition dropped the timezone, ignored row order and mixed
  signals; event ids collided across machines so concat dropped events;
  ValueMapper/ContextEnricher dropped columns and codes; build_paths missed
  the last hour; boolean and string change filters missed edges;
  fill_gaps(max_gap) measured gaps wrongly; lead_times could be negative;
  reliability and stockout returned inf.
fix: crashes on empty, single-row, all-NaN, text-time, nullable and pyarrow
  input across transforms, features, signals, profile, pipeline, event log
  and detectors; a missing column raises ColumnNotFoundError everywhere.
change: canonical parameter names (value_column, time_column, uuid_column,
  window) outside the detectors; old names deprecated. No data gives NaN or
  None, unknown mapping codes are kept. See docs/migration.md.
feat: max_gap on detect_flatline, threshold_with_hysteresis and
  on_off_intervals; contract tests for every public function; the
  invariant harness checks determinism, input mutation, row order,
  output sanity, all-NaN, single-row and duplicate timestamps.

# October 14, 2026 (round 6)
Maturity of the existing functions: same numbers for any data source, fast
on real data sizes, proven against references, traceable outputs.
fix: results no longer depend on how the data was loaded. Nullable,
  pyarrow and text-number columns are converted once (utils.normalize):
  52 methods gave different numbers (counter totals counted the first
  reading as production, downtime per shift was 0) and 172 crashed.
  Rows with the same timestamp are ordered by uuid.
perf: shift reports, maintenance trend fits, routing, warmup and stockout
  methods are vectorized: 10-300x faster on 200k rows (nok_by_shift
  59 s -> 1.3 s, remaining_useful_life 55 s -> 0.2 s).
fix: DST fall-back nights no longer crash window flooring (floor_local);
  duplicate timestamps no longer give infinite rates; single-row and
  constant inputs, all-null/boolean string columns, and window <= 0 in
  oscillation detection are handled.
fix: capability trending uses the spec in force in each window; energy
  methods with weekly/month-end windows report the real window (and
  energy_per_unit / carbon_intensity_per_unit no longer return empty or
  raise); downtime and shift handover read boolean run signals (was 100%
  downtime); flow_constraint_analytics applies its thresholds;
  from_shift_data matches string dates; detect_outlier_sensor needs
  three sensors to name an outlier.
fix: group counter totals add up to the signal total (counter_by_group):
  every shift/day/operator/part/batch boundary lost one increment, and
  alternating operators or parts were double counted (609 parts reported
  on a 360-part counter). FlowConstraintEvents intervals end at the next
  change.
change: KPIs carry their basis (run/planned/excluded seconds, parts,
  resets); every duration has a *_seconds column; seven summary methods
  return a DataFrame (as_frame=False deprecated). See docs/migration.md.
test: reference tests for SPC rules 1-8, Cp/Cpk (Montgomery), GUM,
  Weibull (Lieblein-Zelen) and energy values; property tests; harness
  dtypes check; examples/ and docstring examples run in the test suite.
types: strict mypy flags for the whole library, no exemptions.
fix: cycles, period cutters and object detection verified against
  hand-built scenarios. Cycles: correct pairing for back-to-back and
  nested markers, deterministic cycle_uuid, one half-open boundary rule.
  Segments: the last period keeps its data, boundary samples are cut
  once, segment_by_cut is no longer one piece off. Objects: empty
  readings end a presence, change-encoded ids last to the data end,
  hand-over events link once, pm4py export round-trips. All three are
  10-100x faster.
feat: severity_level (info/warn/critical) next to every categorical
  severity, so detectors can be filtered together; detector labels stay.
fix: lag_profile windows without speed data are NaN (was the lag at
  min_speed); CriticalParameterRankingEvents needs no long-format frame;
  TimeWindowedFeatureTable takes window (freq deprecated).
docs: the site is cut from 139 to 106 pages (14k to 6k lines) and
  rewritten in plain language; every example runs.

# October 14, 2026 (round 5)
Correct by default. Results change wherever data has counter resets,
change-encoded signals, uneven sampling or several signals per frame; the
old behavior stays one argument away. Full list: docs/migration.md
("Correct by default").
change: correct defaults: handle_resets=True, interval_end="next_change",
  time_weighted=True, group_by_uuid=True, slope_per="second",
  missing_as_zero=False, handle_refills=True, SPC include_severity=True,
  regex=False for substring filters, trim_to_range=True in loaders,
  best_and_worst_shifts returns a DataFrame, batch_transition_matrix
  returns a long from/to/count table (layout="wide" for the matrix).
change: full precision: detectors no longer round results.
fix: machine_running_pct is 0-100 (was a 0-1 ratio; see
  machine_running_ratio).
fix: nothing is skipped silently: loaders take errors="warn"|"raise" and
  name unreadable objects; non-numeric values, malformed specs, failed
  fits and untestable windows warn with DataQualityWarning; a missing value
  column raises ColumnNotFoundError naming it.
fix: flatline detection ignores single samples of regularly sampled signals.
change: deprecated (removed in a future major release): OrderTraceabilityEvents, a single
  tolerance_uuid (upper/lower limits now work one-sided), station_map,
  adapt(legacy=), integer window in correlation, as_frame=False. Every
  deprecation message says "removed in the next major release".
feat: OutlierDetectionEvents(signal_uuids=...); parquet extra; [all]
  installs every optional feature.
build: setuptools-scm reads its config (builds were versioned 0.0.0);
  releases publish after the full CI suite, choose their version with a
  Release-Bump / Release-As commit trailer, and are tagged only after the
  upload succeeded; CI builds and checks the package; Python 3.14 is
  fully supported; Development Status: Beta.
types: the mypy quarantine is empty; every module is type-checked.
policy: every merge publishes the next patch version; renamed names keep
  working with a warning and are removed only in a major release.

# October 14, 2026 (round 4)
fix: SPC rules follow Nelson (1984) / Western Electric: rule 8 = 8 in a row
  with none within 1 sigma (was within), rule 3 = 6 points, rule 4 = 14
  points, rules 5/6 count outer zones per side, rule 2 uses the center line;
  rules no longer mutate the input; vectorized == per-rule results.
fix: detectors do what their signatures and docs promise (28 items across
  production, quality, engineering, energy, development, loaders, features):
  ignored parameters now work or warn (slope_window, max_cycle_gap on every
  CycleTimeEvents method, idle_threshold per shift, interval_end on
  long-downtime gap counting and recipe phases, value_column in process
  alignment, ...); silently overridden ones warn (settle_pct vs tol,
  state_map vs station_map); tolerance deviations keep values and single
  samples; takt adherence uses strict > and ignores counter resets; golden
  batch phases compare like with like; hour-folder loaders include the
  end hour; NaN instead of None/0.0 for undefined ratios. Opt-in: time-
  weighted time_in_range, slope per second in drift detection,
  trim_to_range in loaders, regex=False for substring matching. Every
  result change is listed in docs/migration.md.
docs: every public parameter is documented (guard test).
change: consistent input checks for every detector (Base.__init_subclass__ +
  utils.signal_checks): a signal uuid that is not in a non-empty frame emits
  DataQualityWarning naming the available uuids (one per call, not per
  internal sub-call); a missing time or uuid column raises
  ColumnNotFoundError (a ValueError) instead of a bare KeyError.
change: parameters follow docs/naming.md: time_column (was column_name),
  window (freq/period/resample), window_size (integer row counts), n_sigma,
  lower_limit/upper_limit, warning/critical_threshold, include_initial,
  only_*, signal_uuids, run_state_uuid, value_column_<role> in energy. Old
  keywords work with a DeprecationWarning. Optional arguments are
  keyword-only (positional use warns). event_uuid defaults are
  "<pack>:<name>" and unique (outlier_event -> quality:outlier, ...).
  tests/test_naming.py lints every detector signature.
fix: rising-edge detection on an object column of Python bools (~True is
  -2, truthy) counted every True sample as a cycle end in
  CycleTimeEvents.cycle_time_by_part and LineThroughputEvents.takt_adherence /
  cycle_quality_check.
change: one name per concept (docs/naming.md). Detector classes end in
  Events (DowntimeTracking -> DowntimeEvents, OEECalculator -> OEEEvents, 16
  classes); event classes drop calculate_/compute_/process_ verbs (14
  methods, e.g. calculate_oee -> oee, find_gaps -> detect_gaps) and use
  _statistics / _by_window suffixes. Old names are deprecated aliases that
  warn and are removed only in a future major release; the event log resolves them
  (taxonomy.resolve). See docs/migration.md.
fix: every detector output now carries the canonical columns of its shape
  (intervals start/end/duration_seconds/uuid/source_uuid, summaries
  start/end/duration_seconds, points systime/uuid/source_uuid): 276 of 277
  harness-callable methods conform, up from 78. Columns were only added,
  never removed or renamed. The taxonomy declares the shape each method
  really returns (69 corrected). Empty results keep the full column set;
  six methods that returned frames without columns now return the schema.
  The invariant harness checks `shape` and `empty_schema` for every method.
fix: weekly / month-end windows ('W', 'ME', ...) reported the bin's last day
  as `start`; windows now span their real [start, end).
fix: night shifts are one (date, shift) row: production_day=True is the
  default (False keeps calendar-date grouping, which split a night across
  two rows that each mixed two nights); downtime_by_shift,
  performance_by_shift and setup_durations gained the option.
fix: CapabilityTrendingEvents.capability_forecast subtracted the window index
  twice, understating windows_to_threshold for later windows.
add: ShiftReporting.best_and_worst_shifts(as_frame=True); the dict default
  emits a FutureWarning.
add: ReliabilityEvents (events/maintenance/reliability.py) -- reliability
  KPIs from a failure-state signal (fault bit or status codes), built on
  signals.primitives.intervals so states hold until the next change and
  logging gaps (max_gap) are no data: failure_events (interval), mtbf_mttr
  (MTBF, ISO 22400 MTBF, MTTR, availability, failure rate; overall or per
  DST-correct calendar window), weibull_fit (censored MLE: beta, eta, MTTF,
  B10, failure pattern) and reliability_at (conditional reliability and
  hazard).
test: ISO 22400 OEE reference example (A x P x Q = 59.375 %).

# October 14, 2026
add: ChangePointEvents (events/quality/change_point.py) -- offline
  change-point detection: exact PELT (Killick et al. 2012) and binary
  segmentation in plain numpy, Gaussian mean or mean+variance costs, BIC
  penalty; detect_change_points (point) and segment_statistics (interval).
add: MultivariateSPCEvents (events/quality/multivariate_spc.py) -- Hotelling
  T² with Phase I (beta) and Phase II (F) limits on a reference period,
  per-variable contributions, and out-of-control runs with the top
  contributor. Signals aligned backward with an optional max_gap.
fix: Gauge R&R follows the AIAG average-and-range method: AV subtracts the
  repeatability share EV^2/(n*r) (a negative radicand gives AV = 0), and AV
  and PV use K2/K3 = 1/d2* (g = 1) instead of the large-sample d2. Before,
  AV and PV were overstated (2 appraisers: by 25 %), and so were GRR and
  %GRR. Checked against the AIAG worksheet constants.
add: EnergyPerformanceIndicatorEvents.enpi_vs_baseline(baseline="regression")
  -- ISO 50006 energy baseline energy = a + b*units fitted on a fixed
  baseline period (baseline_period= or the first baseline_window windows);
  adds expected_energy_kwh and energy_savings_kwh. Default unchanged.
docs: every page now shows only names that exist (NumericFilter,
  OutlierDetection, MetadataLoader, S3ProxyParquetLoader, NumericCalc and
  wrong module paths replaced with the real API); tests/docs checks every
  ts_shape import and attribute in the docs' code blocks. CI builds the docs
  with mkdocs build --strict (38 warnings fixed). Python 3.14 classifier;
  README and the development guide describe the real CI/release pipeline.
fix: eventlog static-shape event ids hashed the export time, so re-exporting
  the same detector output gave different ocel:eid values; they are now
  stable (ported from the open PR #60).
add: ts_shape.eventlog.describe("Class.method"), has_override() and
  clear_overrides(); severity is coerced to the info/warn/critical
  vocabulary (low/minor -> info, medium/major/warning -> warn,
  high/alarm/error -> critical; anything else warns and becomes <NA>).
add: ts_shape.signals -- SignalSpec / SignalSpecs (dict/YAML round trip),
  infer_specs() with per-field reasons, and spec-driven primitives:
  intervals, increments, per_window, align, windows, shift_windows.
add: ts_shape.profile(df) -- per-signal data-quality and semantics report
  (gaps, resets, duplicates, NaT, missing values, mixed value columns,
  change-encoded and possibly-stuck signals) with .summary() / .issues().
add: recovered unmerged work from earlier sessions:
  AssetHealthIndexEvents, ProductionScheduleAdherenceEvents,
  RootCauseCorrelationEvents (Aug 10 entry below), PowerIntegrator
  (Aug 20 entry below), and the four inline-metrology quality modules
  MeasurementUncertaintyEvents (GUM type A/B, combined/expanded uncertainty,
  budget), EnvironmentalCompensationEvents (correlation, sensitivity,
  compensation, exceedance), CalibrationValidationEvents (linearity,
  calibration curve, recalibration need, stability) and
  SamplingResolutionEvents (quantization, effective resolution, aliasing,
  jitter). All are registered in the taxonomy and covered by the
  invariant harness (260 methods).
fix: PowerIntegrator daily windows on DST days (23 h / 25 h) and
  AssetHealthIndexEvents.maintenance_forecast timezone.
build: lean core install (pandas, numpy, scipy); loader backends are the
  extras [s3], [azure], [api], [sql], [postgres], [yaml], [all].
add: specs= on MachineStateEvents and DowntimeTracking (defaults follow the
  signal's spec; explicit arguments win).
add: registry-wide invariant harness (tests/invariants) pinned to a ratchet
  baseline, and hypothesis property tests for the primitives.
fix: 36 frequency defaults such as window="5m" crashed resample on pandas 3
  ('m' is no longer an offset); now "5min". CycleExtractor durations accept
  any pandas duration string.
fix: OEECalculator daily summaries are tz-aware with real day lengths
  (23 h / 25 h on DST days); BottleneckDetectionEvents, warmup_consistency and
  demand spikes keep the timezone.
fix: MachineStateEvents accepts an empty frame; ToleranceDeviationEvents groups
  events on the actual signal only (other uuids split or merged them).

# October 13, 2026
Weekly screen of the non-production packs (see docs/gap_screening_other_packs.md).
fix: CycleExtractor -- every process_* method returned cycle_end=NaT and
  is_complete=False; cycles now open on rising edges / step entries, never
  close at themselves, keep a tz-aware systime and ignore other uuids.
fix: ToleranceDeviationEvents applied each measurement the next (future)
  tolerance; capability indices use the latest limits, a single tolerance is
  one-sided, and one measurement raises.
fix: ControlLoopHealthEvents / DisturbanceRecoveryEvents align PV with the
  setpoint in force (backward as-of) instead of SP-left / nearest.
fix: FailurePredictionEvents.remaining_useful_life no longer reports RUL 0 for a
  trend moving away from the threshold; NaN samples are skipped.
fix: LeadTimeAnalysisEvents matches deliveries by order id when present.
fix: EnergyEfficiencyEvents.efficiency_comparison summed raw counter readings.
fix: tz-aware systime is preserved across quality/engineering/maintenance/
  supplychain events (no more naive-UTC .values); DateTimeFilter accepts naive
  bounds on tz-aware columns.
fix: MaterialBalanceEvents reported a zero-input window with output as balanced.
fix: OutlierDetectionEvents -- zscore ignores NaN, MAD=0 falls back to mean
  absolute deviation, IsolationForest skips NaN.
fix: SPC CUSUM with sigma 0/NaN returns no shifts (warning); steady-state
  windows with <2 samples are no longer "steady"; demand empty periods are not
  zero demand and calendar periods work in seasonality_summary.
fix: setpoint oscillation crossings through an exact zero counted once;
  detect_startup_adaptive no longer flags a flat idle signal; track_startup_phases
  advances through non-overlapping phases.
add: opt-in handle_resets (energy per-unit/EnPI/carbon/SEC, TimeGroupedStatistics
  'diff'), handle_refills (inventory consumption_rate), interval_end="next_change"
  (threshold, signal quality, degradation, vibration, steady state, segments, low
  stock, correlation breakdown, time_above_threshold), time_weighted (material
  balance, idle energy), max_gap (error_integrals), missing_as_zero (material
  balance), group_by_uuid (outlier detection, boolean/string edge filters,
  TimeGroupedStatistics), inclusive (DateTimeFilter), ambiguous/nonexistent
  (TimezoneShift). Defaults unchanged.
add: helpers utils.counters.counter_per_window, utils.intervals.interval_end_times,
  utils.time_weighted.time_weighted_resample.
add: Base warns (DataQualityWarning) on NaT timestamps.
fix: data_completeness / coverage_by_period use each window's real length (DST
  days are 23 h / 25 h); auto min_gap ignores duplicate timestamps.
fix: lag_correlation / rolling_correlation lag and window in time on the
  regular grid instead of rows after dropna().
fix: detect_flatline starts a run at its first stuck sample; opt-in
  interval_end="next_change" for deadband historians.
fix: GaugeRepeatabilityEvents.repeatability uses within operator x part ranges
  when operator_column is set.
fix: overshoot_metrics undershoot is measured after the setpoint is reached.
add: detect_warmup / detect_cooldown max_pause; detect_startup_adaptive
  baseline_window is now honoured (default None keeps the sample lookback).
# August 20, 2026
add: PowerIntegrator (ts_shape.transform.calculator.power_integration) --
  the missing kW -> kWh step. The events.energy detectors aggregate their
  input with sum(), i.e. they assume energy deltas in kWh; feeding an
  instantaneous active-power tag (e.g. *.Total_Active_Power-kW) in directly
  produced a number that scaled with the logging rate rather than with
  consumption, and a plain mean() was a sample-count average that over-weighted
  bursts. PowerIntegrator.energy_by_window integrates properly -- each sample
  weighted by how long it was valid, segments split at window boundaries
  (a hold from 00:55 to 01:05 books 5 minutes to each hour), locf or linear
  interpolation, max_gap to drop logging outages instead of billing phantom
  energy, power_unit for W/kW/MW tags, and per-signal integration before
  summing (energy adds across machines, power does not). Reports coverage per
  window and omits unmeasured windows rather than reporting them as 0 kWh.
  to_energy_deltas emits the result in the standard ts-shape schema so the
  energy pack can consume it; segment_energy exposes the per-sample audit
  trail. Exported at the top level as ts_shape.PowerIntegrator.

# August 10, 2026
add: AssetHealthIndexEvents (events.maintenance.asset_health_index) -- fuses
  several degradation signals into one weighted 0-100 asset health index using
  the same drift/variance/trend penalty model as
  DegradationDetectionEvents.health_score. health_index reports the dominant
  failure mode and a confidence level (share of configured signal weight that
  had data); health_events emits warning/critical crossings; signal_contributions
  ranks the signals by the health they cost the asset; maintenance_forecast
  extrapolates to a critical threshold and withholds a date when the trend fit
  is weaker than min_confidence. Closes gap 3.1 of the manufacturing-operations
  research doc.
add: ProductionScheduleAdherenceEvents (events.production.schedule_adherence) --
  joins a planned schedule (start/end/planned_qty/order_id) against actual
  production from a monotonic counter. adherence_by_period reports adherence %,
  deviation units, an ahead/on_track/behind/critical_behind band, and the
  units/hour needed to recover in the time left in the period; deviation_events
  emits the breaches; adherence_summary rolls the horizon up weighted by planned
  quantity. Actuals are reset-aware by default (handle_resets=True).
add: RootCauseCorrelationEvents (events.correlation.root_cause_correlation) --
  directed, event-driven counterpart to AnomalyCorrelationEvents: takes another
  detector's events and ranks candidate signals by lagged correlation with the
  event onsets. Only non-negative lags are searched (a cause precedes its
  effect) and the winning lag's p-value is Bonferroni-corrected for the lag
  scan, so noise scanned over many lags is not promoted to a root cause.
  rank_causes / top_cause / lag_profile.

# July 31, 2026
add: OEECalculator.calculate_oee now threads handle_resets to its performance
  and quality components and max_gap to its availability/performance components.
  Previously the top-level OEE could not benefit from reset handling, so a day
  with a counter reset silently dragged OEE toward zero; and availability booked
  a logging/overnight gap (last state = running) as run time. calculate_availability
  and calculate_performance gained a max_gap kwarg. Output schema unchanged.
add: opt-in max_gap on RuntimeAccountingEvents.runtime_summary,
  runtime_per_window, and operating_hours_meter -- a gap to the next sample
  longer than max_gap is treated as a logging outage and excluded, so it no
  longer inflates run time, utilization, or the hour meter.
fix: LineThroughputEvents.throughput_oee honored the documented
  availability_threshold parameter, which was previously accepted but ignored.
  A window now counts as available when its part count reaches
  availability_threshold x target_rate; the new default 0.0 reproduces the old
  "produced anything" behaviour. throughput_oee also gained handle_resets
  (threaded to count_parts).
add: opt-in infer_dwell_from_next on the traceability timelines
  (OrderTraceabilityEvents.build_timeline / current_status /
  station_dwell_statistics, RoutingTraceabilityEvents.build_routing_timeline /
  station_statistics, MultiProcessTraceabilityEvents.build_timeline /
  station_statistics). A station that logs an identifier once on arrival
  otherwise reports a per-station dwell of 0; when set, each visit's end is the
  identifier's next station arrival (in multi_process, only for sequential
  visits -- parallel/overlapping visits keep their own span). Default unchanged.
add: opt-in production_day extended to the remaining shift-grouping methods that
  share _assign_shift -- ScrapTracking.scrap_by_shift, ReworkTracking
  (rework_by_shift, rework_rate), QualityTracking (nok_by_shift,
  daily_quality_summary), OperatorPerformanceTracking.production_by_operator --
  so an overnight shift is one contiguous (date, shift) group. Default (raw
  calendar date) unchanged.
add: opt-in max_gap on DowntimeTracking.downtime_by_shift / availability_trend
  and max_cycle_gap on CycleTimeTracking.cycle_time_by_part and
  PerformanceLossTracking (performance_by_shift, slow_periods,
  performance_trend). A duration taken as next-time minus time otherwise
  bridges logging gaps and stoppages: a logger outage is booked as uptime, and
  a lunch break becomes one giant "cycle" that reads as a huge speed loss.
  Durations above the cap are excluded as "no data". Default (None) unchanged.
add: opt-in time_weighted on BottleneckDetectionEvents.station_utilization
  (and detect_bottleneck / shifting_bottleneck / throughput_constraint_summary)
  and DutyCycleEvents.duty_cycle_per_window. resample().mean() averages sample
  counts, so on change-encoded/irregular signals a station up all hour but
  logged once reads 50-100% by row count; time_weighted weights each sample by
  the gap to the next sample. Default (sample mean) unchanged.
fix: LineThroughputEvents.cycle_quality_check returned inf/NaN deviations when
  the expected cycle time was zero (explicit, or a degenerate zero median); it
  now returns the typed empty frame.
add: ts_shape.utils.shifts (assign_shift, production_dates) -- shared shift
  assignment plus a "production day" that attributes an overnight shift's
  post-midnight readings to the shift's start date. Grouping a night shift
  (22:00->06:00) by raw calendar date splits it across two dates and, within a
  date, spans the whole day so last - first sweeps in other shifts' output
  (a night shift could report ~750% of target).
add: opt-in production_day=True on TargetTracking.compare_to_target,
  ShiftReporting.shift_production, and ShiftHandover.generate_report so overnight
  shifts group as one contiguous interval. Default (calendar date) unchanged.
add: opt-in require_all_stations=True on LineBalancingEvents.balance_metrics so a
  fully stopped station stays in the denominator (a dead station previously read
  as a perfectly balanced line); adds a stations_missing column.
add: opt-in count_initial_active=False on AlarmManagementEvents.alarm_frequency
  to ignore an alarm already active at the start of the data window.
add: ts_shape.utils.intervals.state_intervals -- shared helper that collapses a
  state signal into intervals and closes each one at the next transition when
  interval_end="next_change". The previous "end at the last sample carrying the
  state" logic collapses to zero duration on change-encoded (is_delta) signals
  -- one row per transition -- so downtime, micro-stop and duty-cycle analyses
  silently reported nothing on the library's normal data shape.
add: opt-in interval_end="next_change" (with an optional data_end bound for the
  final open interval) on MachineStateEvents.detect_run_idle /
  state_quality_metrics and LongDowntimeEvents.detect_long_downtime, and as a
  constructor option on MicroStopEvents and DutyCycleEvents. Default
  ("last_sample") is unchanged. On change-encoded signals this recovers
  micro-stop lost time, multi-hour downtimes, and on/off durations that
  previously read as zero. (Traceability dwell shares the collapse but has
  distinct cross-station semantics and is a separate follow-up.)
add: ts_shape.utils.counters (counter_quantity, counter_increments,
  count_resets) -- shared reset-aware handling for monotonic production
  counters that reset to zero at a shift/part change or PLC restart. A naive
  last - first loses all production in a window where a reset happened.
add: opt-in handle_resets=True across the production pack so the reset-aware
  behaviour shipped in PartProductionTracking is available everywhere a
  quantity is derived from a counter: OEECalculator (calculate_performance,
  calculate_quality), LineThroughputEvents.count_parts, TargetTracking,
  ScrapTracking, ReworkTracking, QualityTracking, BatchTrackingEvents,
  OperatorPerformanceTracking, ContinuousProcessAlignment.segment_by_cut,
  ShiftReporting, ShiftHandover, and PeriodSummary. Default behaviour and
  column sets are unchanged; when enabled the result gains a resets column.
  For the by-reason / by-part methods this also corrects the over-count that
  last - first produces when categories alternate over a non-contiguous group.
fix: DowntimeTracking.downtime_by_reason booked running time as downtime (it
  diffed timestamps after filtering to stopped rows, so a stop lasted until
  the next stop). Duration is now computed on the full state timeline before
  filtering, mirroring downtime_by_shift.
fix: QualityTracking.daily_quality_summary returned NaN rates for a zero-total
  day (0/0); both rate divisions are now guarded to 0.0.
fix: LineThroughputEvents.throughput_oee produced NaN/inf performance when the
  target rate was zero (flat counter or explicit 0); it now reports 0.
fix: ShiftReporting.shift_targets produced inf achievement_pct for a zero
  target; now guarded to 0.0, matching ShiftHandover.
add: ChangeoverEvents.detect_changeover gained include_initial (default True
  preserves the current behaviour of emitting the first reading; pass False to
  report only real value changes and drop the synthetic leading event).
docs: docs/production_gap_screening.md -- a full functional-gap screen of the
  production pack, with the remaining themes (interval-duration on
  change-encoded signals, gap-spanning durations, time-weighted utilisation,
  overnight-shift attribution) lined out as scoped follow-ups.

# June 2, 2026
changed: event-log output now trims entirely-empty extra columns. The 9
  canonical core columns (ocel:eid, ocel:activity, ocel:timestamp,
  ts_shape:start_timestamp, ts_shape:duration_s, ts_shape:detector,
  ts_shape:pack, ts_shape:severity, ts_shape:value) are always emitted, so
  every event log keeps an identical append-friendly core schema; per-detector
  extras (<pack>:<col> passthroughs, standard-attr extensions) are dropped when
  all-empty instead of shipped full of NaN.
add: align_columns(*logs) -- reindex multiple EventLogs' events tables to their
  shared column union (core first, extras sorted) so each frame exposes an
  identical column set before appending/stacking. concat(...) already unions
  columns on merge; this is for callers stacking frames themselves.
changed: event-log exporters renamed for a consistent to_event_log* family:
  to_flat_df -> to_event_log_xes, to_ocel_tables -> to_event_log_ocel. The old
  names remain as deprecated aliases (DeprecationWarning) for back-compat.

# May 20, 2026
add: Pipeline -- the single declarative orchestrator for ts-shape. Chains
  transform and detector steps into one reusable definition: .transform()
  steps replace the working signal; .detect() steps store named event
  results. Supports $input/$prev sentinels for multi-DataFrame wiring,
  run_steps() for intermediates, describe() for previews, and
  PipelineResult.to_event_log() for combined OCEL export.
BREAKING: removed FeaturePipeline (features.segment_analysis.feature_pipeline);
  Pipeline replaces it. Migration: FeaturePipeline(df)...run() becomes
  Pipeline()...run(df); .add_step(Cls.method, **kw) and
  .add_instance_step(Cls, call='method', **kw) both become
  .transform(Cls, 'method', **kw); .add_lambda_step(fn) becomes
  .transform(fn). $input/$prev sentinels and run_steps() are unchanged.
add: UnitConverter -- engineering unit conversion (transform), a thin wrapper
  around the pint library. Optional extra: pip install ts-shape[units].
add: RuntimeAccountingEvents -- operating-hours accounting (total run time,
  start count, longest run, utilization, per-window runtime, hour-meter).
add: LineBalancingEvents -- line balancing & takt analysis (station cycle
  times, balance efficiency/delay, smoothness index, theoretical minimum
  stations, Yamazumi station-loading table).
add: FlowMetricsEvents -- flow analytics (time-weighted WIP, throughput,
  FIFO lead time, Little's Law consistency check, Process Cycle Efficiency).
add: ts_shape.make_timeseries() -- public synthetic-data generator for trying
  detectors and transforms without real data or loaders.
add: ts_shape.list_detectors() -- programmatic catalog of every top-level
  class, grouped by pack/category.
add: Base.__repr__ -- detector and instance classes now print a useful
  summary (row count + configured uuid/value_column).
changed: OutlierDetectionEvents emits the canonical point shape (systime,
  uuid, source_uuid) via events/_output.py -- completes the May 14 output
  standardization. uuid carries the event id, source_uuid the source signal.
changed: event detectors validate their configured UUID up front
  (Base._validate_uuid) -- a typo'd UUID now raises a clear error listing the
  available UUIDs instead of silently returning empty results.
fix: DatapointAPI HTTP requests now send a timeout (default 30s) so an
  unresponsive server can no longer hang the loader.
fix: TimescaleDBDataAccess uses bound SQL parameters instead of f-string
  interpolation.
fix: CycleExtractor uses Base's sorted, defensive copy -- it no longer
  mutates the caller's DataFrame.
fix: README quick-start and pack examples corrected to match real
  constructor signatures (SPC, ToleranceDeviationEvents, DataIntegratorHybrid,
  TimeGroupedStatistics).

# May 14, 2026
BREAKING: event-detector outputs standardized across all packs (quality,
  production, engineering, maintenance, supplychain, energy, correlation).
  Every public DataFrame-returning method now emits one of three canonical
  shapes: point (systime), interval (start, end, duration_seconds), or
  summary (start, end, duration_seconds + aggregate columns).

  Column renames applied uniformly:
    start_time, gap_start, window_start, period_start, disturbance_start
      -> start
    end_time, gap_end, window_end, period_end, disturbance_end -> end
    severity_score, severity_level -> severity
    OEECalculator: date -> start + end + duration_seconds (86400)

  New helpers in src/ts_shape/events/_output.py (empty_event_df,
  finalize_point_df, finalize_interval_df, finalize_summary_df) define
  the canonical column constants and empty-frame schema.

  ts_shape.eventlog.adapters.adapt no longer probes legacy column name
  variants -- it expects the canonical names. The taxonomy registry has
  been updated (severity_field="severity"). See docs/guides/eventlog.md
  for the simplified shape table.

  Downstream code asserting old column names must be updated.

# Sep 09, 2025
add: Azure blob storage container loader
add: Metadata JSON loader added
changed: development guide

# Apr 06, 2025
add: quickstart guide added to docs

# Dec 26, 2024
add: mkdocs material deployment with gh actions

# Dec 25, 2024
add: pdoc docs exchanged with mkdocs material autodoc

# Dec 23, 2024
add: library rename
add: library structure change. loader, transform, feature, context, events

# Dec 20, 2024
fix: closes #7

# Nov 16, 2024
add: dev state docs for combine/integrator.py
add: dev state for combine/integrator.py
add: metadata loader improved

# Nov 4, 2024
add: stats classes improved + feature table class added

# Oct 29, 2024
add: timescaledb loader adjusted

# Oct 28, 2024
add: loader classes adjusted
add: classes for timestamp_converter added
add: classes for s3, timescaledb, timezone_shift added

# Oct 27, 2024
add: docs for time_stats added
add: time_stats for numeric value columns added

# Sep 11, 2024
add: methods refactored to execute without an instance

# Sep 9, 2024
add: cycle methods refined and splitted to cycle processor and extractor

# Sep 8, 2024
add: cycle method and metadata loader including tests for numeric filters
add: additional methods added

# Aug 25, 2024
fix: docs re-created
fix: docs
add: parquet loader, timestamp, string, numeric and boolean stats methods

# May 4, 2024
change: typings added, first test added
change: package folder moved
init: pypi package structure for timeseries-shaper / internal project for my master thesis