Skip to main content
Version: 3.0.1

False KL Checklist: Before You Tag @KNOWN_LIMITATION

Created: 2026-03-03 Status: Living document -- update as new false-KL patterns are discovered

Approximately 41% of @KNOWN_LIMITATION tags applied in Waves 1-3 were later found to be incorrect. This checklist exists to prevent that pattern. Consult it every time before applying the tag.


1. Mandatory Checklist

Before applying @KNOWN_LIMITATION, answer ALL of these. If any answer is "no" or "I haven't checked," investigate further before tagging.

  • Can you modify the config with PhpSpreadsheet? PhpSpreadsheet (PHP, NOT Python/openpyxl) can edit ANY xlsx sheet -- Target Settings, Combined Outcomes, Error Resolutions, Rules Mapping, QIR, Mix Setup. Write a PHP CLI script at code/scripts/. The only restriction: openpyxl (Python) delete_rows() corrupts Rules Mapping sheets. PhpSpreadsheet does not have this problem.

  • Can you use a different base config? Quest EZ PP, Viracor v30, Viracor v31, Nottingham, Quest Mpox each have different characteristics. A TV infeasible with one config may be trivial with another.

  • Does the Behat step actually exist? Check code/features/bootstrap/FeatureContext.php directly -- the dev testing guide may be out of date. Key undocumented step: Given the client configuration "{key}" is set to "{value}" (toggles client config flags like use_sample_type after import).

  • Can you engineer the JSON fixture differently? CT discrepancy is computed from |machine_ct - dxai_ct| > configured delta -- it is observation-level, not a config setting. Classification comes from readings arrays, not dxai_cls. Control pass/fail is determined by fixture ct, cls, and readings values.

  • Is multi-run working? Yes. BT-9517 has 51 uploads. BT-9533 does 3-run sequences. "Complex multi-run fixture" is engineering work, not a limitation.

  • Could a cross-rule error approach work? BT-9621 QSSC solved a "catch-22" KL by using ADJ_CT as a cross-rule error source.

  • Can rule execution order be changed? Rule priority is configurable via the rule priority column in the Rules Mapping sheet. If a higher-priority rule blocks your test, create a config variant with different priorities.

  • Can you add a browser test instead? Behat/Mink browser tests work well (331 scenarios passing). Visual/UI verification KLs should become browser tests, not KLs.

  • Have you checked control label properties? The has_extraction boolean on Roles determines whether a control becomes ExtractionControlWell or ControlWell. This affects which rules (MINCONTROL vs MINEXTRACT) can match. Check the Roles sheet before declaring extraction-related TVs as KL.

  • Can you add a delay between operations? For multi-run tests, now()->toDateTimeString() has second-level precision. A 2-second wait between uploads ensures different created_at values. Check if an I wait N seconds step exists (BrowserContext has one; add to FeatureContext if missing).

  • Does the config have resolution codes for the error? If the scenario resolves wells, the config's Error Resolutions sheet must have resolution code rows for the specific error code on the well. Zero resolution codes = "Resolution is not allowed." This is a config authoring issue, not a code limitation.


2. Top 8 False Assumptions That Lead to Wrong KL Tags

2.1 "PhpSpreadsheet re-save risks corruption"

PhpSpreadsheet handles xlsx editing correctly. The corruption issue is openpyxl's delete_rows() on Rules Mapping. Agents confuse the two tools. PhpSpreadsheet is PHP; openpyxl is Python. Use PhpSpreadsheet.

2.2 "No standard config has this setup"

Create config variants. Write a PHP script to modify the config. This is expected workflow, not a limitation. Every wave has created config variants for specific TVs.

2.3 "SpecimenNameValidator blocks use_sample_type=FALSE"

Use Given the client configuration "use_sample_type" is set to "false" to toggle AFTER import. This step was added during TI-003 resolution specifically to solve this problem.

2.4 "Deferred: complex fixture engineering needed"

Complexity is not a Known Limitation. Multi-run, multi-well, multi-config scenarios are proven patterns. BT-9517 (51 uploads), BT-9533 (3-run sequences), BT-9536 (multi-mix with 5 targets) all demonstrate this.

2.5 "No Behat step for config import validation"

Config load failures return HTTP errors. Invalid configs can be tested by attempting load and asserting failure. Check FeatureContext.php for current step definitions.

2.6 "EC cascade blocks my test"

EC cascade only fires when extraction controls FAIL. Make EC controls pass in your fixture. EC cascade is caused by incorrect rule mapping, incorrect control result, or missing controls -- all controllable via fixtures.

2.7 "Can't get Amb/specific classification"

Classification comes from readings. If you set dxai_cls, you must use matching dxai_ct (Pos needs a ct, Neg means no amplification). Engineer the readings to produce the classification you need.

HOWEVER: null CT + Pos cls and CT=35 + Neg cls are genuinely impossible (DXAI enforces consistency). These ARE valid KLs.

2.8 "Only one non-IC target in config"

Multiple configs have multi-target mixes: Quest Mpox, Quest COVID, Nottingham, Viracor. Check existing configs before assuming single-target.

2.9 "WDCLS/WDCLSC must be mapped for classification tests"

Not always. The AMB rule adds a CLASSIFICATION problem to observations without setting a well error or preventing analysis. For Combined Outcome tests involving Amb classification (especially on controls), map AMB instead of WDCLSC, and set preferred_result_provider = DXAI. The Merk config demonstrates this pattern for HPV control wells.

2.10 "Resolution is not allowed -- must be a code limitation"

No. The config XLSX Error Resolutions sheet must have resolution codes for the error code on the well being resolved. If the sheet has zero rows for that error code (and zero patient-level codes), the API rejects the resolution with "Resolution is not allowed for the selected well." This is a config authoring issue, not a code limitation. Fix: add resolution code rows to the Error Resolutions sheet for the relevant error code, or use a config that already has them.


3. Historical Precedents (Resolved False KLs)

Original ClaimResolutionLesson
TI-002: "EC cascade blocks 45 scenarios"All passed with original config. 45 stale tags removed.EC cascade is controllable via fixtures
TI-003: "SpecimenValidator blocks use_sample_type=FALSE"Added 1 Behat step to toggle client config after importCheck for new step defs before declaring KL
QSSC TV-001-004: "Catch-22: no error = no resolution"Solved by using ADJ_CT as cross-rule error sourceCreative cross-rule approaches work
SWCOMBOUT TV-003/009: "No multi-mix CO achievable"Solved by using Viracor Noro configTry different base configs
SYSINH TV-018/025: "ISSUE-015 EC cascade blocks"EC cascade was not involved -- wrong ISSUE tag. Real issue is code assigns "Detected" instead of INHNVerify root cause before attributing to known issues
COMBOUTCTRL-008: "Amb CLS on control always triggers WDCLSC first"Map AMB rule (not WDCLSC) + set preferred_result_provider=DXAI. Merk config proves this works.Don't assume WDCLSC must be mapped -- AMB adds CLASSIFICATION problem without setting a well error
EXTCTRL TV-006-001→007: "MINCONTROL and MINEXTRACT inseparable"Set has_extraction=false on control roles in config → ControlWell created → MINCONTROL matches independentlyCheck control label has_extraction property -- it's the architectural toggle separating MINCONTROL/MINEXTRACT code paths
REPEATSAMP TV-001-015: "Laravel microsecond truncation → non-deterministic ordering"Add I wait 2 seconds step between uploads (already exists in BrowserContext, just missing from API FeatureContext)Missing Behat step ≠ Known Limitation. Check if the step exists in BrowserContext before declaring KL
WDCT TV-014/015: "No config with custom CT delta"Created config variant with delta=5 via PhpSpreadsheetConfig variants are expected workflow, not limitations
COMBOUT TV-004-001/002: "ctDiscrepancy not configurable"Created config with ct_discrepancy=YES via PhpSpreadsheetSame -- config modification is standard approach
MINFL TV-001-005: "No config with null min_fluor"Created config with null minimum fluorescence via PhpSpreadsheetSame
RQUAL-ERR-001 + RQUANTQUAL-002-009: "COVID reporting blocks test"Removed COVID reporting rows from config via PhpSpreadsheetRemove irrelevant config rows rather than declaring limitation
SYSINH resolution scenarios: "Resolution is not allowed"Config had 0 resolution codes for SYSTEMIC_INHIBITON_DETECTED error codeCheck Error Resolutions sheet has codes for the error you are resolving

4. When KL IS Appropriate

KL is correct when:

  • Code path is genuinely unreachable via integration testing -- defensive guards or normalization prevent the state from being reached through external inputs.
  • PHPUnit is the only way to exercise the path -- e.g., null IC observation (normalizer guard prevents it), WfinalclsRule override (internal dispatch).
  • DXAI enforces physics constraints -- null CT + Pos cls is impossible because amplification produces CT.
  • TV requires UI verification that cannot be done via API -- Assay Summary display grouping, visual indicators. But consider adding a browser test first (Section 1, item 8).
  • Per-observation dispatch prevents cross-observation aggregation -- e.g., PicqualSerumRule checks each non-IC observation independently. If the TV requires "any non-IC positive → pass" but code only checks "this non-IC positive → pass", the outcome is unreachable. However, verify this matters in practice -- real multi-pathogen clients may use a different rule (e.g., INH) that doesn't have this limitation.

5. Step Definition Gap Reference

For TVs blocked by missing Behat step definitions, see the Dashboard's "Step Definition Backlog" section in v3-testing-dashboard.md. That section tracks which steps are missing, how many tests each step would unlock, and prioritization.

Do not tag scenarios as @KNOWN_LIMITATION when the real issue is a missing step definition. Tag them as @NEEDS_STEP_DEF instead, and add an entry to the Step Definition Backlog if one does not already exist.