Skip to content

Fix four latent pipeline bugs found in a full-pass review - #35

Merged
BasLinders merged 1 commit into
mainfrom
fix-latent-pipeline-bugs
Aug 21, 2026
Merged

Fix four latent pipeline bugs found in a full-pass review#35
BasLinders merged 1 commit into
mainfrom
fix-latent-pipeline-bugs

Conversation

@BasLinders

@BasLinders BasLinders commented Aug 21, 2026

Copy link
Copy Markdown
Owner

Summary

Four correctness bugs found in a full-pipeline review, each verified empirically/by direct reproduction before fixing, with a regression test added per fix.

  • prox/discovery.py — Inductive Miner discovery called inductive_miner.apply() without variant=Variants.IMf, so noise_threshold had zero effect: noise filtering only exists in the IMf variant, not the default IM variant this code invoked. Verified: identical process trees at noise_threshold=0.0 vs 0.9 under IM; IMf at noise_threshold=0.0 matches plain IM exactly, so switching is safe at the default too. The UI's "Noise Threshold" slider had been a no-op the whole time.

  • prox/conformance.py_fitness_state_equation_alignments() accepted initial_marking/final_marking arguments but never used them, instead guessing markings from net topology ("place with no in-arcs" = initial, "no out-arcs" = final). Usually coincides with the real markings for a discovered net (which is why this went unnoticed), but a place with no arcs at all satisfies both conditions and gets folded into both guessed markings, and any reference-model topology (build_structured_reference_model/import_reference_model_bpmn) where the real start/end doesn't coincide with sourceless/sinkless places silently corrupts fitness/alignments. Now uses the real markings passed in — same pattern the sibling _fitness_token_replay() already used — falling back to the topology guess only if none were supplied.

  • prox/analytics.py — purchase/cart/research-keyword matching built a regex via '|'.join(values) with no guard for an empty list: '|'.join([]) is '', and str.contains('') matches every row. An empty purchase_values/cart_values/research_keywords list (reachable via business_params or direct function arguments) silently classified everything as a match instead of nothing, across 5 call sites — including classify_sessions() added earlier this session. Extracted into a shared _contains_any() helper that returns an all-False mask for an empty list.

  • prox/data_manager.pyrefine_activity_labels() decided whether to apply URL-cleaning (strip query string, keep last path segment) based only on the first matched row's value, then applied that one decision to the entire column. A column mixing plain and URL-like values leaked raw slashes/query strings into every non-first-style row's activity name. The cleaning steps are no-ops for genuinely plain values, so they're now applied unconditionally per row instead of gated on a single sampled value.

Test plan

  • pytest tests/ — 140 passed (5 new regression tests, one per fix, each reproducing the original failure against the pre-fix code before confirming the fix)
  • Verified each fix independently against the concrete failure scenario described above (noise threshold now changes discovered nets; a decoy sourceless/sinkless place no longer corrupts alignment markings; empty keyword lists now match nothing; a mixed plain/URL context column is now cleaned consistently per row)
  • Ran the full pipeline end-to-end against generated mock data with noise_threshold=0.3 — completes cleanly, all result keys present, conformance scores in expected range

- discovery.py: Inductive Miner discovery called inductive_miner.apply()
  without variant=Variants.IMf, so noise_threshold had zero effect -
  noise filtering only exists in the IMf variant. Verified empirically
  (identical trees at noise=0.0 vs 0.9 under the default IM variant,
  and IMf at noise=0.0 matches plain IM exactly, so this is safe at
  the default too). The UI's Noise Threshold slider had been a no-op.

- conformance.py: _fitness_state_equation_alignments() accepted
  initial_marking/final_marking but never used them, instead guessing
  markings from net topology ("no in-arcs" = initial, "no out-arcs" =
  final). Usually coincides with the real markings for a discovered
  net, but a place with no arcs at all satisfies both conditions and
  gets folded into both guessed markings - and any reference-model
  topology (build_structured_reference_model/import_reference_model_
  bpmn) where the real start/end doesn't coincide with sourceless/
  sinkless places silently corrupts fitness/alignments. Now uses the
  real markings, same pattern as the sibling _fitness_token_replay(),
  falling back to the topology guess only if none were supplied.

- analytics.py: purchase/cart/research-keyword matching built a regex
  via '|'.join(values) with no guard for an empty list - '|'.join([])
  is '', and str.contains('') matches every row. An empty
  purchase_values/cart_values/research_keywords list (a reachable
  business_params/function-argument value) silently classified
  everything as a match instead of nothing, across 5 call sites
  including this session's classify_sessions(). Extracted into a
  shared _contains_any() helper that returns an all-False mask for an
  empty list.

- data_manager.py: refine_activity_labels() decided whether to apply
  URL-cleaning (strip query string, keep last path segment) based only
  on the first matched row's value, then applied that single decision
  to the entire column - a column mixing plain and URL-like values
  leaked raw slashes/query strings into every non-first-style row's
  activity name. The cleaning steps are no-ops for genuinely plain
  values, so they're now applied unconditionally per row instead of
  gated on a single sampled value.

Added a regression test for each.
@BasLinders BasLinders self-assigned this Aug 21, 2026
@BasLinders BasLinders added the bug Something isn't working label Aug 21, 2026
@BasLinders
BasLinders marked this pull request as ready for review August 21, 2026 12:16
@BasLinders
BasLinders merged commit 52b8275 into main Aug 21, 2026
1 check passed
@BasLinders
BasLinders deleted the fix-latent-pipeline-bugs branch August 21, 2026 12:18
BasLinders added a commit that referenced this pull request Sep 8, 2026
Fix four latent pipeline bugs found in a full-pass review
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant