Bug Isolation & Responsible Disclosure: A QA Case Study
A multi-year unreproducible defect, isolated, documented, and disclosed: moving a bug report from 'cannot reproduce' to confirmed, then publishing the fix to the public so no user had an unfair edge.
Summary
A defect in the platform’s pitch count assignment system had gone unresolved across two separate bug reports over several years. Neither internal QA nor the development team could identify the triggering conditions. I returned to the problem, systematically isolated the root cause, documented a fully repeatable reproduction sequence, and delivered a structured report that moved the bug from “cannot reproduce” to “confirmed, backlogged.” I then authored a public disclosure post to ensure all users had equal access to the known workaround.
Context
WhatIfSports is a browser-based sports simulation platform with a substantial competitive user base. The platform enforces a 40-pitch-count minimum for pitchers assigned to starting roles — a constraint that existed for legitimate simulation integrity reasons but had become increasingly misaligned with how modern baseball (and the community’s advanced strategies) actually operates.
A multi-year user report described an edge case in which the pitch count minimum could be bypassed, but no one — including the development team — had been able to reproduce it reliably. The bug was effectively in limbo: real enough to have been filed twice, unactionable for lack of a reproduction path.
The Problem
Pitchers assigned to starting roles were subject to a 40-pitch minimum in the Advanced Settings UI. Attempts to set lower counts were silently overridding this 40-pitch threshold. However, user reports indicated that lower-than-minimum counts were sometimes persisting — inconsistently, and with no clear trigger.
The defect had two layers:
1. Functional: Pitch count settings entered before a role change were not being re-validated after the role change — leaving the engine running values the UI would never have allowed directly.
2. Cosmetic: The display showed “none/none” after the workaround sequence, masking the actual live setting and creating a dangerous mismatch between what the user saw and what the engine was executing.
Process
Step 1 — Hypothesis Formation
Previous attempts had focused on the save action itself. I shifted focus to the sequence of operations across multiple UI windows — specifically, what happens when a save state persists in one window while a structural change (role reassignment) is made in another. The hypothesis: the bug was a state synchronization failure, not a validation failure on the save.
Step 2 — Isolation
I tested the hypothesis by methodically varying the sequence of actions across the Advanced Settings window and the Manager’s Center window, holding all other variables constant. The triggering sequence was identified:
| 1. Set PC in Reliever Role | Configure the desired pitch count settings for the pitcher while assigned to a reliever role. Save. This creates a valid save state with settings the UI accepts. |
| 2. Leave Window Open | Keep the Advanced Settings window open without closing it. This is the critical condition — the window holds its save state in memory. |
| 3. Reassign Role | Switch to Manager’s Center and move the pitcher to a starter role. The role change occurs, but the Advanced Settings window — still open — does not re-validate against the new role’s constraints. |
| 4. Re-save Without Changes | Return to the Advanced Settings window (the original one, not a new instance) and re-save without modifying any settings. The engine commits the previously saved reliever settings to the starter role, bypassing the 40-pitch minimum. |
| 5. Display Mismatch | The UI now displays “none/none” for the pitch count — the cosmetic layer has detected an invalid state and blanked the display. The engine, however, continues running the previously committed values. |
Step 3 — Reproducibility Confirmation
The sequence reproduced the behavior 100% of the time under controlled conditions. I tested variations to establish the full boundary of the bug:
- Confirmed: Any pitch count value below the 40-pitch minimum could be committed this way.
- Confirmed: The committed values persisted through game simulations and were actively applied by the engine.
- Confirmed: Re-saving the Advanced Settings after the role change (even without modifications) overwrote the committed values with “none/none,” triggering default behavior — effectively destroying the configuration silently.
- Confirmed: Any subsequent save to Advanced Settings would reset the pitcher to default behavior, requiring the full sequence to be repeated.
Reporting & Disclosure
Internal Report
I delivered the reproduction sequence, boundary conditions, and both the functional and cosmetic components of the defect to the development team. The report moved the bug from “cannot reproduce” to confirmed. The team added it to their backlog and assessed it as low-priority given the scope of impact and the niche conditions required to trigger it.
I also raised the question of whether the correct fix was patching the sequence or reconsidering the 40-pitch minimum itself — given the growing prevalence of openers, bullpen games, and non-traditional pitching usage in MLB, the constraint the bug bypassed was itself worth reviewing. The team noted the feedback. [Editor’s note: when the fix was implemented, they reduced the minimum to 20-pitches for pitchers assigned a starting role.]
Public Disclosure
”I’ve been asked about it in every league I’ve tested it and used it, and I told admin I’d share in the forums so it would be fair to everyone.”
Once the internal report was complete and the team had confirmed and accepted the bug, I published a full disclosure post to the platform’s community forums. The post included:
- The complete reproduction sequence, in plain language
- The critical warnings about actions that would silently destroy the configuration
- A live example demonstrating the behavior in a real league context
- A cross-reference to related research (appearance fatigue) showing the practical implications
The reasoning was straightforward: the bug was real, it was in use, and selective knowledge of it created an uneven playing field. Fairness required disclosure. This is the same calculus I apply in any environment where a known vulnerability or edge case affects multiple stakeholders — internal confirmation first, then structured public communication.
Outcome
| Bug status | Moved from “cannot reproduce” → confirmed, backlogged |
| Reproducibility | 100% under documented sequence |
| Disclosure method | Internal report to dev team → coordinated public community disclosure |
| Root cause | State synchronization failure across concurrent UI windows; save state not re-validated on role change |
| Secondary finding | Cosmetic display (“none/none”) masked live engine state — a silent failure mode more dangerous than the original bug |
What This Demonstrates
While the domain here is a baseball simulation game. The process is applicable in any environment.
The same approach applies to any system where a defect has resisted prior investigation: shift the hypothesis, change what you hold constant, map the state transitions across boundaries rather than within them, and document precisely enough that someone who wasn’t there can reproduce and verify.