Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/interpreting_candidate_scores.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ positive components:
| `mapping_confidence_score` | 3 | trust in identifier harmonization (worst mapping among contributing records) | `mapping_confidence` |
| `quality_score` | 2 | study/data quality (`1 - n_quality_flags/5`, floored at 0) | `quality_flags` |

Every quality flag counts the same in `quality_score`: `batch_confounded`
(treatment fully confounded with a sequencing run, MS acquisition batch, or
extraction batch) removes the same 0.2 as `low_replication`, and counts
toward the `quality_score >= 0.4` high-priority gate the same way. Adding the
term (2026-10-16) did not change any existing score, because no registered
study declared it yet. Once a study declares it, every candidate with a
record from that study loses 0.2 `quality_score` (0.4 points of the 0-100
score) unless the flag was already in its union.

### Why the unbounded components saturate instead of clipping

Every component whose input has no natural upper bound uses `v / (v + half)`,
Expand Down
2 changes: 2 additions & 0 deletions registry/controlled_vocabularies/quality_flags.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ terms:
label: Species assignment not confirmed by genotyping (required for species in a hybridizing complex, e.g. Mytilus edulis complex, unless species_identity.basis is genotyped)
- id: reference_species_mismatch
label: Reads were mapped to a related species' genome; gene identifiers belong to the reference species, not the study animal
- id: batch_confounded
label: Treatment contrast fully confounded with a processing batch (sequencing run, MS acquisition batch, extraction batch); treatment effect cannot be separated from batch
13 changes: 13 additions & 0 deletions tests/test_prioritize.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,19 @@ def test_compute_components_bounds_are_0_to_1():
assert 0.0 <= value <= 1.0, f"{key}={value} out of bounds"


def test_batch_confounded_costs_at_least_as_much_quality_as_low_replication():
meta_row = {
"k_studies": 2, "total_sample_size": 24, "pooled_effect": 1.0,
"p_value": 1e-4, "direction_consistency": 1.0, "phenotype": "survival",
"distinct_tissues": 1, "distinct_life_stages": 1, "i_squared": 0.0,
}
baseline = compute_components(meta_row, ["exact"], [])["quality_score"]
low_rep = compute_components(meta_row, ["exact"], ["low_replication"])["quality_score"]
batch = compute_components(meta_row, ["exact"], ["batch_confounded"])["quality_score"]
assert batch < baseline
assert batch <= low_rep


def test_high_priority_gate_blocks_low_direction_consistency_even_with_two_studies(isolated_reports):
harmonize_all_demo_studies()
meta_df = run_meta_analysis(phenotype="larval_viability", feature_type="gene")
Expand Down
13 changes: 13 additions & 0 deletions tests/test_schema_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,19 @@ def test_unknown_vocabulary_term_is_rejected(tmp_path):
assert any("phenotype" in e for e in result.errors)


def test_batch_confounded_quality_flag_validates(tmp_path):
with open("registry/studies/GIGAS_HEAT01.yaml") as fh:
study = yaml.safe_load(fh)
study["study_id"] = "BATCH_FLAG"
study["quality_flags"] = [*(study.get("quality_flags") or []), "batch_confounded"]
path = tmp_path / "BATCH_FLAG.yaml"
with open(path, "w") as fh:
yaml.safe_dump(study, fh)

result = validate_study_file(path)
assert result.valid, result.errors


def test_resilience_classification_mismatch_is_a_warning_not_an_error(tmp_path):
with open("registry/studies/GIGAS_HEAT01.yaml") as fh:
study = yaml.safe_load(fh)
Expand Down
Loading