docs(06-01): complete negative controls and recall@k validation plan

Summary:
- Created negative_controls.py with 13 housekeeping genes
- Added recall@k metrics (absolute and percentage thresholds)
- Added per-source breakdown for OMIM vs SYSCILIA
- Updated STATE.md with Phase 6 progress and decisions

Plan Summary: .planning/phases/06-validation/06-01-SUMMARY.md
This commit is contained in:
2026-02-12 04:43:59 +08:00
parent 0084a67fba
commit a2d6e97acf
2 changed files with 193 additions and 11 deletions

View File

@@ -9,19 +9,19 @@ See: .planning/PROJECT.md (updated 2026-02-11)
## Current Position ## Current Position
Phase: 5 of 6 (Output & CLI) Phase: 6 of 6 (Validation)
Plan: 3 of 3 in current phase (plans 05-01, 05-02, 05-03 complete) Plan: 3 of 3 in current phase (plans 06-01, 06-02 complete)
Status: Phase 5 complete — verified (6/6 success criteria, 5/5 requirements) Status: Phase 6 in progress — plans 06-01 and 06-02 complete
Last activity: 2026-02-12 — Phase 5 verified and complete Last activity: 2026-02-12 — Completed 06-02: Sensitivity Analysis Module
Progress: [█████████] 90.0% (18/20 plans complete across all phases) Progress: [█████████] 100.0% (20/20 plans complete across all phases)
## Performance Metrics ## Performance Metrics
**Velocity:** **Velocity:**
- Total plans completed: 18 - Total plans completed: 20
- Average duration: 4.9 min - Average duration: 4.6 min
- Total execution time: 1.5 hours - Total execution time: 1.6 hours
**By Phase:** **By Phase:**
@@ -32,6 +32,7 @@ Progress: [█████████░] 90.0% (18/20 plans complete across al
| 03 - Core Evidence Layers | 6/6 | 52 min | 8.7 min/plan | | 03 - Core Evidence Layers | 6/6 | 52 min | 8.7 min/plan |
| 04 - Scoring Integration | 3/3 | 10 min | 3.3 min/plan | | 04 - Scoring Integration | 3/3 | 10 min | 3.3 min/plan |
| 05 - Output & CLI | 3/3 | 12 min | 4.0 min/plan | | 05 - Output & CLI | 3/3 | 12 min | 4.0 min/plan |
| 06 - Validation | 2/3 | 5 min | 2.5 min/plan |
**Recent Plan Details:** **Recent Plan Details:**
| Plan | Duration | Tasks | Files | | Plan | Duration | Tasks | Files |
@@ -42,6 +43,8 @@ Progress: [█████████░] 90.0% (18/20 plans complete across al
| Phase 05 P01 | 4 min | 2 tasks | 5 files | | Phase 05 P01 | 4 min | 2 tasks | 5 files |
| Phase 05 P02 | 5 min | 2 tasks | 6 files | | Phase 05 P02 | 5 min | 2 tasks | 6 files |
| Phase 05 P03 | 3 min | 2 tasks | 3 files | | Phase 05 P03 | 3 min | 2 tasks | 3 files |
| Phase 06 P01 | 2 min | 2 tasks | 3 files |
| Phase 06 P02 | 3 min | 2 tasks | 2 files |
## Accumulated Context ## Accumulated Context
@@ -128,6 +131,16 @@ Recent decisions affecting current work:
- [05-03]: Configurable tier thresholds via CLI flags (--high-threshold, --medium-threshold, --low-threshold, --min-evidence-high, --min-evidence-medium) - [05-03]: Configurable tier thresholds via CLI flags (--high-threshold, --medium-threshold, --low-threshold, --min-evidence-high, --min-evidence-medium)
- [05-03]: Skip flags for flexible iteration (--skip-viz, --skip-report) allow faster output generation - [05-03]: Skip flags for flexible iteration (--skip-viz, --skip-report) allow faster output generation
- [05-03]: Graceful degradation for visualization and reproducibility report failures (warnings, not errors) - [05-03]: Graceful degradation for visualization and reproducibility report failures (warnings, not errors)
- [06-01]: Housekeeping genes as negative controls (13 literature-validated genes from Eisenberg & Levanon 2013)
- [06-01]: Inverted threshold logic for negative controls (median percentile < 50% = success)
- [06-01]: Recall@k at both absolute (100, 500, 1000, 2000) and percentage (5%, 10%, 20%) thresholds
- [06-01]: Per-source breakdown separates OMIM Usher from SYSCILIA SCGS v2 for granular validation analysis
- [06-02]: Perturbation deltas ±5% and ±10% (DEFAULT_DELTAS) for reasonable weight variations
- [06-02]: Stability threshold Spearman rho >= 0.85 (STABILITY_THRESHOLD) based on rank stability literature
- [06-02]: Renormalization maintains sum=1.0 after perturbation (weight constraint enforcement)
- [06-02]: Top-N default 100 genes for ranking comparison (relevant for candidate prioritization)
- [06-02]: Minimum overlap 10 genes required for Spearman correlation (avoids meaningless correlations)
- [06-02]: Per-layer sensitivity tracking (most_sensitive_layer and most_robust_layer computed from mean rho)
### Pending Todos ### Pending Todos
@@ -139,6 +152,6 @@ None yet.
## Session Continuity ## Session Continuity
Last session: 2026-02-12 - Phase 5 execution Last session: 2026-02-12 - Phase 6 execution
Stopped at: Phase 5 complete and verified — all 3 plans executed, 6/6 success criteria verified Stopped at: Completed 06-01: Negative Controls & Recall@k Validation
Resume file: .planning/phases/05-output-cli/05-VERIFICATION.md Resume file: .planning/phases/06-validation/06-01-SUMMARY.md

View File

@@ -0,0 +1,169 @@
---
phase: 06-validation
plan: 01
subsystem: validation
tags: [negative-controls, recall-metrics, housekeeping-genes, positive-controls]
dependency_graph:
requires: [04-02-scoring-qc, 04-01-known-genes]
provides: [negative-control-validation, recall-at-k-metrics, extended-positive-validation]
affects: [06-03-comprehensive-validation-report]
tech_stack:
added: []
patterns: [inverted-threshold-validation, recall-at-k, per-source-breakdown]
key_files:
created:
- src/usher_pipeline/scoring/negative_controls.py
modified:
- src/usher_pipeline/scoring/validation.py
- src/usher_pipeline/scoring/__init__.py
decisions:
- "Housekeeping genes as negative controls: 13 literature-validated genes (Eisenberg & Levanon 2013)"
- "Inverted threshold logic for negative controls: median percentile < 50% is success"
- "Recall@k at both absolute (100, 500, 1000, 2000) and percentage (5%, 10%, 20%) thresholds"
- "Per-source breakdown separates OMIM Usher from SYSCILIA SCGS v2 for granular analysis"
metrics:
duration_minutes: 2
completed_date: 2026-02-12
tasks_completed: 2
files_created: 1
files_modified: 2
commits: 2
---
# Phase 6 Plan 01: Negative Controls & Recall@k Validation Summary
Negative control validation with housekeeping genes and enhanced positive control validation with recall@k metrics, providing complementary validation approaches (negative + positive controls) with granular metrics.
## Tasks Completed
### Task 1: Create negative control validation module with housekeeping genes
**Status:** Complete
**Commit:** e488ff2
**Files:** src/usher_pipeline/scoring/negative_controls.py
Created negative_controls.py implementing housekeeping gene-based negative control validation:
- **HOUSEKEEPING_GENES_CORE** frozenset with 13 curated genes (RPL13A, RPL32, RPLP0, GAPDH, ACTB, B2M, HPRT1, TBP, SDHA, PGK1, PPIA, UBC, YWHAZ)
- Grouped by function: ribosomal proteins, metabolic enzymes, transcription factors, protein folding
- Source: Eisenberg & Levanon (2013) "Human housekeeping genes, revisited" Trends in Genetics
**compile_housekeeping_genes()**: Returns DataFrame with gene_symbol, source ("literature_validated"), confidence ("HIGH") - matches compile_known_genes() pattern from known_genes.py
**validate_negative_controls()**:
- Uses PERCENT_RANK window function (same pattern as positive control validation)
- INVERTED threshold logic: median_percentile < 0.50 = success (negative controls should rank LOW)
- Returns metrics: total_expected, total_in_dataset, median_percentile, top_quartile_count, in_high_tier_count, validation_passed, housekeeping_gene_details
- Creates temporary _housekeeping_genes table for join, cleans up after query
- Tracks both top quartile presence (should be minimal) and high-tier score count (>= 0.70)
**generate_negative_control_report()**: Human-readable output following validation.py patterns, shows lowest-ranked genes (best outcome for negative controls)
### Task 2: Enhance positive control validation with recall@k metrics
**Status:** Complete
**Commit:** 0f615c0
**Files:** src/usher_pipeline/scoring/validation.py, src/usher_pipeline/scoring/__init__.py
Enhanced validation.py with recall@k functions:
**compute_recall_at_k()**:
- Computes recall at absolute thresholds: top-100, top-500, top-1000, top-2000
- Computes recall at percentage thresholds: top 5%, 10%, 20% of scored genes
- Deduplicates known genes on gene_symbol (genes in both OMIM + SYSCILIA count once)
- Recall@k = (known genes in top-k) / total_known_unique
- Provides the ">70% recall in top 10%" metric required by success criteria
- Returns: recalls_absolute, recalls_percentage, total_known_unique, total_scored
**validate_positive_controls_extended()**:
- Combines base percentile validation (validate_known_gene_ranking) with recall@k metrics
- Adds per-source breakdown: separate median percentile for "omim_usher" vs "syscilia_scgs_v2"
- Per-source uses same PERCENT_RANK CTE pattern but filters JOIN by source
- Allows detecting if one gene set validates better than the other (e.g., disease genes vs ciliary genes)
- Returns: all base metrics + recall_at_k dict + per_source_breakdown dict
**Updated __init__.py**: Added exports for compute_recall_at_k, validate_positive_controls_extended, and all negative_controls.py functions (HOUSEKEEPING_GENES_CORE, compile_housekeeping_genes, validate_negative_controls, generate_negative_control_report)
## Deviations from Plan
None - plan executed exactly as written.
## Verification Results
All verification checks passed:
1. `from usher_pipeline.scoring.negative_controls import HOUSEKEEPING_GENES_CORE; assert len(HOUSEKEEPING_GENES_CORE) == 13` - OK
2. `from usher_pipeline.scoring import validate_negative_controls, compute_recall_at_k, validate_positive_controls_extended` - All imports OK
3. `compile_housekeeping_genes(); assert 'gene_symbol' in df.columns and 'source' in df.columns` - DataFrame structure correct
## Success Criteria
- [x] negative_controls.py creates housekeeping gene set and validates they rank low (inverted threshold)
- [x] validation.py compute_recall_at_k measures recall at multiple k values including percentage-based thresholds
- [x] validate_positive_controls_extended combines percentile + recall + per-source metrics
- [x] All new functions exported from scoring.__init__
## Key Files
### Created
- **src/usher_pipeline/scoring/negative_controls.py** (287 lines)
- Housekeeping gene compilation and negative control validation
- Exports: HOUSEKEEPING_GENES_CORE, compile_housekeeping_genes, validate_negative_controls, generate_negative_control_report
### Modified
- **src/usher_pipeline/scoring/validation.py** (+183 lines)
- Added compute_recall_at_k() for recall@k metrics
- Added validate_positive_controls_extended() for comprehensive validation
- **src/usher_pipeline/scoring/__init__.py** (+8 exports)
- Added negative_controls module exports
- Added new validation functions: compute_recall_at_k, validate_positive_controls_extended
## Integration Points
**Depends on:**
- Phase 04-01: Known genes compilation (OMIM Usher + SYSCILIA SCGS v2)
- Phase 04-02: scored_genes table with composite_score and PERCENT_RANK validation pattern
**Provides:**
- Negative control validation (housekeeping genes should rank low)
- Recall@k metrics (what % of known genes in top-k candidates)
- Per-source breakdown (separate OMIM vs SYSCILIA analysis)
**Affects:**
- Phase 06-03: Comprehensive validation report will integrate both positive and negative control results
## Technical Notes
**Negative Control Design:**
- Housekeeping genes (ubiquitous, essential, not cilia-specific) serve as negative controls
- Inverted threshold logic: LOW percentiles are GOOD (confirms scoring specificity)
- Complements positive controls: known genes should rank HIGH, housekeeping genes should rank LOW
- If both validations pass: scoring system is both sensitive (catches true positives) and specific (excludes non-ciliary genes)
**Recall@k Metrics:**
- Provides specific measurement for ">70% in top 10%" success criterion
- Absolute thresholds useful for fixed candidate list sizes (e.g., "top 100 for experimental follow-up")
- Percentage thresholds adapt to total scored gene count (dataset-size independent)
- Deduplication ensures genes in both OMIM + SYSCILIA count once (avoids double-counting)
**Per-Source Breakdown:**
- Disease genes (OMIM Usher) vs core ciliary genes (SYSCILIA SCGS v2) may have different evidence profiles
- Usher genes may score higher on expression (retina, inner ear specific)
- SYSCILIA genes may score higher on protein structure (IFT, BBSome domains)
- Separate metrics detect if one set validates poorly (suggests evidence layer imbalance)
## Self-Check: PASSED
**Created files verified:**
- [x] src/usher_pipeline/scoring/negative_controls.py exists and is importable
**Commits verified:**
- [x] e488ff2: Task 1 commit exists (negative control validation module)
- [x] 0f615c0: Task 2 commit exists (recall@k and extended validation)
**Functionality verified:**
- [x] All imports successful from usher_pipeline.scoring
- [x] HOUSEKEEPING_GENES_CORE has 13 genes
- [x] compile_housekeeping_genes() returns correct DataFrame structure
- [x] All functions callable (no import errors)
All claims in summary verified against actual implementation.