STD: CT Cutoff Rule (WFINALCLS)
Version: v1.0.0
Status: Draft
SRS Source: docusaurus/docs/srs/rules/rule-ct-cutoff-wfinalcls.md
Rule Name: WFINALCLS
Domain: RULES-CTCUTOFF
Overview
This document specifies tests for the CT Cutoff rule (WFINALCLS) using decision tables and test vectors. The rule evaluates CT (Cycle Threshold) values against configured cutoff boundaries to determine final classification, with specimen-type-aware reporting selection and asymmetric NULL value handling.
Rule Characteristics:
- Pure business logic (no UI)
- Threshold-based classification (CT > cutoff = Negative)
- Specimen-type-aware reporting configuration selection
- Asymmetric NULL handling (NULL CT = Negative, NULL Quantity = preserve)
- Error reporting for missing configuration
Test Method: TM-API (per Test Plan section 3.3 - Rules use automated API tests)
Verification Approach:
Rule verification is performed using data-driven test vectors. Each row in a decision table represents a complete verification scenario with defined inputs and expected outputs. This format enables exhaustive condition coverage while remaining concise and auditable.
Coverage Summary
| REQ ID | Title | Conditions | Test Vectors | Coverage | Gaps |
|---|
| REQ-RULES-CTCUTOFF-001 | CT Exceeds Cutoff Sets Negative | 6 | 8 | 100% | None |
| REQ-RULES-CTCUTOFF-002 | Specimen-Based Reporting Selection | 4 | 6 | 100% | None |
| REQ-RULES-CTCUTOFF-003 | NULL CT Sets Negative | 3 | 4 | 100% | None |
| REQ-RULES-CTCUTOFF-004 | NULL Quantity Preserves Classification | 3 | 4 | 100% | None |
| REQ-RULES-CTCUTOFF-005 | Missing Config Error | 3 | 4 | 100% | None |
Totals: 5 REQs, 19 Conditions, 26 Test Vectors, 100% Coverage
| Variable | Type | Valid Values | Description |
|---|
obs.final_ct | float/null | null, 0-50+ | Observation CT value |
obs.final_quantity | float/null | null, numeric | Observation quantity value |
obs.target_id | int/null | null, valid ID | Target identifier |
obs.specimen_type | string | Plasma, Serum, etc. | Specimen type of observation |
obs.current_cls | string | Positive, Negative, Ambiguous | Current classification before rule |
config.ct_cutoff | float | 10, 30, 40, etc. | Configured CT cutoff threshold |
config.use_sample_type | bool | true, false | Enable specimen-type-aware reporting |
reporting.specimen_type | string | Plasma, Serum, etc. | Reporting configuration specimen type |
reporting.ct_upper_boundary | float | 10, 30, 40, etc. | Reporting CT upper boundary |
reporting.last_modified | timestamp | datetime | Last modification timestamp |
Output Variables
| Variable | Type | Description |
|---|
obs.machine_cls | string | Machine-determined classification |
obs.dx_ai_cls | string | Diagnostic AI classification |
well.error_code | string/null | Error code (CUTOFF_LIMITS_MISSED) |
classification_modified | bool | Whether classification was changed |
REQ-RULES-CTCUTOFF-001: CT Exceeds Cutoff Sets Negative
Acceptance Criteria:
- AC1: When CT > cutoff, set machine_cls to Negative
- AC2: When CT > cutoff, set dx_ai.cls to Negative
- AC3: When CT > cutoff, set BOTH machine_cls AND dx_ai.cls to Negative
- AC4: When CT <= cutoff, do not modify classification
- AC5: Cutoff value shall be configurable per target
- AC6: Default CT cutoff threshold shall be 40
Decision Table: CT Threshold Evaluation
| TV | obs.final_ct | config.ct_cutoff | obs.current_cls | machine_cls | dx_ai_cls | cls_modified | Covers |
|---|
| TV-001-001 | 45 | 40 | Positive | Negative | Negative | true | AC1, AC2, AC3: CT exceeds cutoff |
| TV-001-002 | 41 | 40 | Positive | Negative | Negative | true | AC1, AC2, AC3: CT just over cutoff |
| TV-001-003 | 35 | 40 | Positive | Positive | (unchanged) | false | AC4: CT within cutoff |
| TV-001-004 | 40 | 40 | Positive | Negative | Negative | true | AC1, AC2, AC3: CT equals cutoff (boundary, code uses >=) |
| TV-001-005 | 39.99 | 40 | Positive | Positive | (unchanged) | false | AC4: CT just under cutoff |
| TV-001-006 | 45 | 30 | Ambiguous | Negative | Negative | true | AC1, AC2: Different cutoff value |
| TV-001-007 | 45 | 40 | Negative | Negative | Negative | false | AC1, AC2: Already negative |
| TV-001-008 | 50 | 40 | Positive | Negative | Negative | true | AC6: Default cutoff behavior |
Decision Table: Configurable Cutoff Per Target
| TV | target_id | config.ct_cutoff | obs.final_ct | expected_outcome | Covers |
|---|
| TV-001-009 | Target_A | 30 | 35 | Negative | AC5: Target-specific cutoff |
| TV-001-010 | Target_B | 40 | 35 | (preserve) | AC5: Different target, different cutoff |
REQ-RULES-CTCUTOFF-002: Specimen-Based Reporting Selection
Acceptance Criteria:
- AC1: When use_sample_type enabled, match specimen type to corresponding reporting config
- AC2: When use_sample_type disabled, use last modified reporting config
- AC3: Plasma(CT=25, cutoff=30) = Positive, Serum(CT=25, cutoff=10) = Negative when enabled
- AC4: Both use Serum cutoff=10 when disabled (last modified)
Decision Table: Specimen Type Matching
| TV | use_sample_type | obs.specimen_type | plasma_cutoff | serum_cutoff | last_modified | obs.final_ct | expected_cls | Covers |
|---|
| TV-002-001 | true | Plasma | 30 | 10 | Serum | 25 | Positive | AC1, AC3: Plasma uses Plasma cutoff |
| TV-002-002 | true | Serum | 30 | 10 | Serum | 25 | Negative | AC1, AC3: Serum uses Serum cutoff |
| TV-002-003 | false | Plasma | 30 | 10 | Serum | 25 | Negative | AC2, AC4: Uses last modified (Serum) |
| TV-002-004 | false | Serum | 30 | 10 | Serum | 25 | Negative | AC2, AC4: Uses last modified (Serum) |
| TV-002-005 | false | Plasma | 30 | 10 | Plasma | 25 | Positive | AC2: Uses last modified (Plasma) |
| TV-002-006 | true | Plasma | 30 | 10 | Serum | 35 | Negative | AC1: Plasma CT > Plasma cutoff |
REQ-RULES-CTCUTOFF-003: NULL CT Sets Negative
Acceptance Criteria:
- AC1: NULL CT value shall set classification to Negative
- AC2: NULL CT handling applies regardless of other field values
- AC3: NULL CT is treated as boundary exceeded (asymmetric with NULL Quantity)
Decision Table: NULL CT Handling
| TV | obs.final_ct | obs.final_quantity | obs.current_cls | config.ct_cutoff | expected_cls | cls_modified | Covers |
|---|
| TV-003-001 | NULL | 1000 | Positive | 40 | Negative | true | AC1: NULL CT sets Negative |
| TV-003-002 | NULL | NULL | Positive | 40 | Negative | true | AC2: NULL CT regardless of Qty |
| TV-003-003 | NULL | 1000 | Ambiguous | 40 | Negative | true | AC1: NULL CT from Ambiguous |
| TV-003-004 | NULL | 1000 | Negative | 40 | Negative | false | AC1: NULL CT already Negative |
Decision Table: Asymmetric NULL Comparison
| TV | null_field | expected_behavior | Covers |
|---|
| TV-003-005 | CT | Set Negative (boundary exceeded) | AC3: Asymmetric handling |
| TV-003-006 | Quantity | Preserve classification (boundary NOT passed) | AC3: Asymmetric handling |
REQ-RULES-CTCUTOFF-004: NULL Quantity Preserves Classification
Acceptance Criteria:
- AC1: NULL Quantity shall NOT modify classification
- AC2: Existing classification shall be preserved when Quantity is NULL
- AC3: NULL Quantity is treated as boundary NOT passed (asymmetric with NULL CT)
Decision Table: NULL Quantity Handling
| TV | obs.final_ct | obs.final_quantity | obs.current_cls | config.ct_cutoff | expected_cls | cls_modified | Covers |
|---|
| TV-004-001 | 35 | NULL | Positive | 40 | Positive | false | AC1, AC2: NULL Qty preserves Positive |
| TV-004-002 | 35 | NULL | Negative | 40 | Negative | false | AC1, AC2: NULL Qty preserves Negative |
| TV-004-003 | 35 | NULL | Ambiguous | 40 | Ambiguous | false | AC1, AC2: NULL Qty preserves Ambiguous |
| TV-004-004 | 45 | NULL | Positive | 40 | Negative | true | CT > cutoff overrides Qty NULL |
Decision Table: Combined NULL Scenarios
| TV | obs.final_ct | obs.final_quantity | expected_behavior | Covers |
|---|
| TV-004-005 | NULL | NULL | Set Negative (CT NULL takes precedence) | AC3: CT NULL wins |
| TV-004-006 | 35 | NULL | Preserve classification | AC3: Qty NULL preserves |
| TV-004-007 | 45 | NULL | Set Negative (CT > cutoff) | CT threshold takes effect |
REQ-RULES-CTCUTOFF-005: Missing Config Error
Acceptance Criteria:
- AC1: target_id = NULL shall set CUTOFF_LIMITS_MISSED error
- AC2: Error code shall be exactly: CUTOFF_LIMITS_MISSED
- AC3: Error shall be set when observation cannot be matched to any configured cutoff limits
Decision Table: Error Conditions
| TV | obs.target_id | config_exists | expected_error | error_code | Covers |
|---|
| TV-005-001 | NULL | N/A | true | CUTOFF_LIMITS_MISSED | AC1, AC2: NULL target_id |
| TV-005-002 | 999 | false | true | CUTOFF_LIMITS_MISSED | AC3: Config not found |
| TV-005-003 | 1 | true | false | null | Normal processing |
| TV-005-004 | 1 | true | false | null | Valid target with config |
Decision Table: Error Code Exactness
| TV | error_condition | expected_error_code | Covers |
|---|
| TV-005-005 | target_id is NULL | CUTOFF_LIMITS_MISSED | AC2: Exact code |
| TV-005-006 | config not found | CUTOFF_LIMITS_MISSED | AC2: Exact code |
Boundary Value Analysis
CT Threshold Boundaries
| Boundary | Test Value | Expected | Test Vector |
|---|
| Below cutoff | 39 (cutoff=40) | Preserve | TV-001-003 |
| At cutoff | 40 (cutoff=40) | Negative | TV-001-004 |
| Just above cutoff | 40.01 (cutoff=40) | Negative | TV-001-002 |
| Well above cutoff | 45 (cutoff=40) | Negative | TV-001-001 |
NULL Value Boundaries
| Field | NULL Value | Expected | Test Vector |
|---|
| CT | NULL | Negative | TV-003-001 |
| Quantity | NULL | Preserve | TV-004-001 |
| Both | NULL | Negative (CT wins) | TV-004-005 |
Test Implementation
Test File Locations
| Requirement | Test File | Method | Automation Status |
|---|
| REQ-RULES-CTCUTOFF-001 | tests/Unit/Rules/WfinalclsRuleTest.php | TM-API | Automated |
| REQ-RULES-CTCUTOFF-002 | tests/Unit/Rules/WfinalclsRuleSpecimenTypeTest.php | TM-API | Automated |
| REQ-RULES-CTCUTOFF-003 | tests/Unit/Rules/WfinalclsRuleNullCtTest.php | TM-API | Automated |
| REQ-RULES-CTCUTOFF-004 | tests/Unit/Rules/WfinalclsRuleNullQuantityTest.php | TM-API | Automated |
| REQ-RULES-CTCUTOFF-005 | tests/Unit/Rules/WfinalclsRuleErrorTest.php | TM-API | Automated |
Traceability to Existing Tests
| Requirement | Jira Tests | Status | Notes |
|---|
| REQ-RULES-CTCUTOFF-001 | BT-5354 | Existing | Behat test (Quest_PP_2_22.xlsx config) |
| REQ-RULES-CTCUTOFF-002 | BT-4283 | Existing | Manual test (12 steps) |
| REQ-RULES-CTCUTOFF-003 | BT-5354 | Partial | Covers TV-003-001, TV-003-005; other TVs are gaps |
| REQ-RULES-CTCUTOFF-004 | — | Gap | No existing test coverage |
| REQ-RULES-CTCUTOFF-005 | BT-4167 | Existing | Manual test (9 steps) |
Gap Analysis
Identified Gaps
| Gap | Requirement | Description | Priority | Owner |
|---|
| GAP-001 | REQ-RULES-CTCUTOFF-003 | BT-5354 covers TV-003-001 and TV-003-005; TV-003-002 through TV-003-004 need test coverage | Medium | TBD |
| GAP-002 | REQ-RULES-CTCUTOFF-004 | No existing test coverage for NULL Quantity handling | Medium | TBD |
-
GAP-001: Create additional test cases for:
- TV-003-002 (NULL CT + NULL Qty)
- TV-003-003 (NULL CT from Ambiguous state)
- TV-003-004 (NULL CT already Negative)
-
GAP-002: Create test cases for NULL Quantity handling:
- TV-004-001 through TV-004-007 (NULL Quantity scenarios)