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
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: help demo test lint ci clean bench-leakage bench-multi-negatives bench-baseline bench-hidden-active bench-cluster-split bench-expert-ablation bench-expert-ablation-500 bench-selectivity bench-feature-decomp bench-gate bench-easy-baseline bench-charge-matched bench-order-dependent bench-precision-at-k bench-per-family bench-simulation-ablation bench-simulation-ablation-within-amp simulation-gate regenerate-all generate phase3 pilot validate-scoring validate-scoring-phase3 validate-scoring-strict external-pilot pilot-confident presynth-qc gold-standard diversity synthesis-order novelty-broad external-consensus questionnaire gate-check ip-report benchmark-card wave0-5-gate-check wave0-5-novelty-audit wave0-5-novelty-audit-v2 wave0-5-panel wave0-5-evidence wave0-5-fill-external wave0-5b-generate wave0-5b-filter recalibration-engine recalibration-engine-dry-run validate-policy-version generate-synthetic-lab-results bump-policy-version generate-review-packet failed-candidate-report safe-publication-filter classify-negative-informativeness
.PHONY: help demo test lint ci clean bench-leakage bench-multi-negatives bench-baseline bench-hidden-active bench-cluster-split bench-expert-ablation bench-expert-ablation-500 bench-selectivity bench-feature-decomp bench-gate bench-easy-baseline bench-charge-matched bench-order-dependent bench-precision-at-k bench-per-family bench-simulation-ablation bench-simulation-ablation-within-amp simulation-gate regenerate-all generate phase3 pilot validate-scoring validate-scoring-phase3 validate-scoring-strict external-pilot pilot-confident presynth-qc gold-standard diversity synthesis-order novelty-broad external-consensus questionnaire gate-check ip-report benchmark-card wave0-5-gate-check wave0-5-novelty-audit wave0-5-novelty-audit-v2 wave0-5-panel wave0-5-evidence wave0-5-fill-external wave0-5b-generate wave0-5b-filter recalibration-engine recalibration-engine-dry-run validate-policy-version generate-synthetic-lab-results bump-policy-version generate-review-packet failed-candidate-report safe-publication-filter classify-negative-informativeness validate-rejection-events negative-result-dashboard

PYTHON := $(shell [ -f .venv/bin/python ] && echo .venv/bin/python || echo python3)
PYTEST := $(shell [ -f .venv/bin/pytest ] && echo .venv/bin/pytest || echo pytest)
Expand Down Expand Up @@ -620,3 +620,9 @@ validate-rejection-events:
--input examples/rejection_events_example.json \
--out-json outputs/rejection_events_validation.json \
--out-md outputs/rejection_events_validation.md

negative-result-dashboard:
PYTHONPATH=src $(PYTHON) scripts/negative_result_dashboard.py \
--input examples/negative_result_dashboard_example.json \
--out-json outputs/negative_result_dashboard.json \
--out-md outputs/negative_result_dashboard.md
6 changes: 4 additions & 2 deletions docs/evidence/METRICS_CURRENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ Machine-readable snapshot: `outputs/metrics_snapshot.json` regenerated with `mak
> **Purpose:** One authoritative table of current pipeline metrics. If any doc disagrees
> with this file, this file wins. Updated whenever benchmark/benchmark config changes.
>
> **Last updated:** 2026-07-09 (Phase F F8 — bulk rejection-event validator — v0.5.79)
> **Last updated:** 2026-07-09 (Phase F F9 — negative-result dashboard — v0.5.79)
> **New in v0.5.79:** Negative-result dashboard (F9) — CLI (`scripts/negative_result_dashboard.py`) that reads a collection of negative-result entries from a JSON file and produces a structured dashboard with summary statistics (by category, by pipeline version), score distributions (activity, safety, novelty, ensemble), per-category cross-analysis, and pipeline insights (most common failure category, recalibration opportunities). Outputs JSON + Markdown. Example file (`examples/negative_result_dashboard_example.json`) with 15 toy entries across all 6 failure categories. Schema (`schemas/negative_result_dashboard.schema.json`). `make negative-result-dashboard` target. 33 tests. 2883 total.
>
> **New in v0.5.79:** Bulk rejection-event validator (F8) — CLI (`scripts/validate_rejection_events.py`) that reads a JSON list of rejection events, validates each `rejection_code` against the rejection taxonomy (`examples/rejection_taxonomy_example.json`), checks required fields (candidate_id, rejection_code, date, pipeline_version), and outputs a PASS/FAIL validation report with per-event errors, rejection-code distribution, and Markdown summary. Example file (`examples/rejection_events_example.json`) with 6 toy events. `make validate-rejection-events` target. 26 tests. 2849 total.
>
> **New in v0.5.78:** Calibration link from negative-result entries to intake reports (F7) — closes learning loop by tracing each negative-result entry back to its prediction-vs-actual data in the calibration intake report. Schema addition: `intake_report_id` optional field on `negative_result_entry.schema.json`. CLI (`scripts/link_negative_result_to_intake.py`) reads negative-result archive JSON + calibration intake report, links by candidate_id, reports matched/unmatched entries, orphan intake candidates, lab summary for matched entries, and validates intake_report_id references. Produces structured JSON + Markdown link report. 25 tests. 2822 total.
Expand Down Expand Up @@ -68,7 +70,7 @@ Machine-readable snapshot: `outputs/metrics_snapshot.json` regenerated with `mak
> **New in v0.5.31:** Added dipeptide-order features for sequence-order awareness. `dipeptide_order_score` achieves AUROC 0.7861 on AMP-vs-scrambled discrimination — the strongest order-dependent feature in the pipeline. Only 7/31 features survive scrambling (amphipathicity/helix-wheel + dipeptide). All composition features are purely position-independent (exactly 0.5000 AUROC on scrambled test).
> **New in v0.5.30:** Easy baseline benchmark added — charge density alone (AUROC 0.8166) outperforms the full pipeline ensemble (0.7792) on AMP-vs-Swiss-Prot-decoy discrimination. Honest finding documented: expected because pipeline optimizes for safety, not raw discrimination.
> **New in v0.5.29:** Expanded benchmark to 500 AMPs + 500 composition-matched decoys (n=1000). AUROC 0.7792 (CI₉₅: 0.7505–0.8065) confirms signal generalizes. Cluster-aware CI: 0.746–0.8102. Representative AUROC: 0.778. Standard benchmark (n=191) retained for backward comparison.
> **Pipeline version:** v0.5.79
> **Pipeline version:** v0.5.79 (F9)
> **Branch:** main

---
Expand Down
54 changes: 54 additions & 0 deletions docs/research/ROADMAP.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,59 @@
# Roadmap

## v0.5.79 — Loop 79: Phase F F9 — Negative-Result Dashboard ✓ (2026-07-09)

CLI that reads a collection of negative-result entries from a JSON file and
produces a structured dashboard with summary statistics, score distributions,
per-category cross-analysis, and pipeline insights. Shows the value of
failures by making failure patterns reviewable at a glance.

Changes:
- `scripts/negative_result_dashboard.py` (F9) — Standalone CLI that loads
negative-result entries (list or dict with `entries` key), builds a dashboard
with category breakdown, pipeline version distribution, per-score statistics
(mean, min, max, std), per-category score cross-analysis, and insights (most
common failure category, recalibration opportunities). Supports `--out-json`
and `--out-md` output. Exit 0 on success, 2 on input errors.
- `schemas/negative_result_dashboard.schema.json` (F9) — JSON Schema Draft
2020-12 for the dashboard output. Validates dashboard_metadata, summary
(by_category, by_pipeline_version), scores_distribution (activity, safety,
novelty, ensemble with mean/min/max/std), category_score_summary, insights
(most_common_failure_category, recalibration_opportunities), and _caveat.
- `examples/negative_result_dashboard_example.json` (F9) — Toy example with 15
entries spanning all 6 failure categories (pre_selection_reject, selected_untested,
lab_inactive, lab_toxic, control_failure, synthesis_failure) across 9 pipeline
versions. Clearly marked EXAMPLE — NOT REAL DATA.
- `Makefile` — Added `negative-result-dashboard` target.
- `tests/evidence/test_negative_result_dashboard.py` — 33 tests covering:
dashboard required structure, summary counts, by-category breakdown,
by-pipeline-version distribution, score distribution stats (min, max, mean,
count), insights (most common category, highest activity category, lowest
safety category, recalibration opportunities), empty entries handling,
entries without scores, category score summary shape, markdown sections
(title, summary, category table, score distribution, insights, caveat),
example file loading, list/dict input formats, missing file error, empty
list error, missing required fields error, CLI exit codes (0, 2), JSON
output writing, markdown output writing, and example file round-trip.
- `docs/evidence/METRICS_CURRENT.md` — v0.5.79 F9 changelog. Test count: 2883.
- `tests/test_test_count_regression.py` — baseline updated to 2883.

Honest boundaries:
- Dashboard summaries are computational aggregations of pipeline failure
records, not biological measurements.
- A category with many entries may reflect pipeline prioritisation choices
(e.g., more candidates pass through pre-selection than reach lab testing)
rather than intrinsic biological failure rates.
- Score distributions reflect pipeline-predicted scores, not measured
activity, safety, or novelty.
- The "most common failure category" insight is a descriptive count, not
evidence that the pipeline is failing correctly or incorrectly.
- Recalibration opportunity counts track entries flagged with
`recalibration_used=yes`; they do not measure whether recalibration
actually improved predictions.
- The dashboard does not determine whether rejected candidates would have
been biologically active or safe. All conclusions require qualified
human review and wet-lab validation.

## v0.5.79 — Loop 78: Phase F F8 — Bulk Rejection-Event Validator ✓ (2026-07-09)

CLI that validates rejection events against the rejection taxonomy. Checks
Expand Down
252 changes: 252 additions & 0 deletions examples/negative_result_dashboard_example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
{
"schema": "EXAMPLE — NOT REAL DATA. For demonstration only.",
"description": "Toy negative-result entries for dashboard demonstration. All candidates, sequences, scores, and reasons are synthetic and must not be treated as real experimental results.",
"pipeline_version": "v0.5.79",
"entries": [
{
"entry_id": 101,
"date": "2026-06-01",
"candidate_id": "DASH_EXAMPLE-001",
"sequence": "RRIRIIRRIRIIRRI",
"reason_category": "pre_selection_reject",
"reason_detail": "Sequence exceeds maximum allowed charge threshold of +6 (net charge +8). Flagged by pre-selection charge filter.",
"pipeline_version": "v0.5.70",
"source_batch": "wave0.5",
"score_activity": 0.82,
"score_safety": 0.35,
"score_novelty": 0.55,
"score_ensemble": 0.72,
"recalibration_used": "no"
},
{
"entry_id": 102,
"date": "2026-06-03",
"candidate_id": "DASH_EXAMPLE-002",
"sequence": "GLLRRLRDFLKKLG",
"reason_category": "pre_selection_reject",
"reason_detail": "Patent near-match flagged (Tanimoto similarity 0.89 to patent WO2020XXXXX). Referred for IP review.",
"pipeline_version": "v0.5.71",
"source_batch": "wave0.5",
"score_activity": 0.79,
"score_safety": 0.72,
"score_novelty": 0.31,
"score_ensemble": 0.68,
"recalibration_used": "no"
},
{
"entry_id": 103,
"date": "2026-06-05",
"candidate_id": "DASH_EXAMPLE-003",
"sequence": "KLAKLAKKLAKLAK",
"reason_category": "selected_untested",
"reason_detail": "Candidate selected for Wave 0.5 panel but deprioritised due to budget constraints. Remains in pool for future waves.",
"pipeline_version": "v0.5.72",
"source_batch": "wave0.5",
"score_activity": 0.85,
"score_safety": 0.90,
"score_novelty": 0.48,
"score_ensemble": 0.79,
"recalibration_used": "no"
},
{
"entry_id": 104,
"date": "2026-06-10",
"candidate_id": "DASH_EXAMPLE-004",
"sequence": "RWQWRWQWRWQWRW",
"reason_category": "lab_inactive",
"reason_detail": "MIC > 128 ug/mL against E. coli, S. aureus, and P. aeruginosa. Positive control (polymyxin B) showed expected MIC 1-2 ug/mL. Three biological replicates all negative.",
"pipeline_version": "v0.5.73",
"source_batch": "wave0.5",
"assay_type": "MIC",
"assay_result": ">128",
"assay_unit": "ug/mL",
"score_activity": 0.76,
"score_safety": 0.88,
"score_novelty": 0.42,
"score_ensemble": 0.71,
"recalibration_used": "no"
},
{
"entry_id": 105,
"date": "2026-06-12",
"candidate_id": "DASH_EXAMPLE-005",
"sequence": "GIGKFLHSAKKWGKAFVGEIMNS",
"reason_category": "lab_inactive",
"reason_detail": "MIC > 256 ug/mL against all tested strains. Magainin-2 analog with single-point mutations expected to retain activity but showed complete loss. Structural analysis pending.",
"pipeline_version": "v0.5.73",
"source_batch": "wave0.5",
"assay_type": "MIC",
"assay_result": ">256",
"assay_unit": "ug/mL",
"score_activity": 0.81,
"score_safety": 0.85,
"score_novelty": 0.39,
"score_ensemble": 0.74,
"recalibration_used": "no"
},
{
"entry_id": 106,
"date": "2026-06-15",
"candidate_id": "DASH_EXAMPLE-006",
"sequence": "FLPIVAGKLLKGL",
"reason_category": "lab_toxic",
"reason_detail": "HC50 = 8 ug/mL (hemolytic). Hemolysis > 50% at 25 ug/mL. Flagged as unsafe for systemic administration.",
"pipeline_version": "v0.5.74",
"source_batch": "wave0.5",
"assay_type": "hemolysis_RBC",
"assay_result": "8",
"assay_unit": "ug/mL HC50",
"score_activity": 0.88,
"score_safety": 0.42,
"score_novelty": 0.52,
"score_ensemble": 0.76,
"recalibration_used": "yes"
},
{
"entry_id": 107,
"date": "2026-06-18",
"candidate_id": "DASH_EXAMPLE-007",
"sequence": "KWRRWRRWKWRRWR",
"reason_category": "lab_toxic",
"reason_detail": "Cytotoxicity CC50 = 4 ug/mL against HEK293 cells. Selectivity index < 1.0. High membrane disruption activity confirmed.",
"pipeline_version": "v0.5.74",
"source_batch": "wave0.5",
"assay_type": "cytotoxicity_mammalian",
"assay_result": "4",
"assay_unit": "ug/mL CC50",
"score_activity": 0.91,
"score_safety": 0.28,
"score_novelty": 0.58,
"score_ensemble": 0.78,
"recalibration_used": "yes"
},
{
"entry_id": 108,
"date": "2026-06-20",
"candidate_id": "DASH_EXAMPLE-008",
"sequence": "AAAAKAAAAKAAAA",
"reason_category": "control_failure",
"reason_detail": "Positive control (polymyxin B) MIC = 32 ug/mL against E. coli (expected 1-2 ug/mL). Assay batch invalidated. Candidate not tested.",
"pipeline_version": "v0.5.74",
"source_batch": "wave0.5",
"assay_type": "MIC",
"score_activity": 0.45,
"score_safety": 0.95,
"score_novelty": 0.22,
"score_ensemble": 0.50,
"recalibration_used": "no"
},
{
"entry_id": 109,
"date": "2026-06-22",
"candidate_id": "DASH_EXAMPLE-009",
"sequence": "RRWQWRMKKLG",
"reason_category": "synthesis_failure",
"reason_detail": "SPPS purification yield < 5%. Multiple hydrophobic stretches caused aggregation during synthesis. Three synthesis attempts all failed.",
"pipeline_version": "v0.5.75",
"source_batch": "wave0.5",
"score_activity": 0.73,
"score_safety": 0.80,
"score_novelty": 0.61,
"score_ensemble": 0.70,
"recalibration_used": "no"
},
{
"entry_id": 110,
"date": "2026-06-25",
"candidate_id": "DASH_EXAMPLE-010",
"sequence": "ACDEFGHIKLMNPQRSTVWY",
"reason_category": "synthesis_failure",
"reason_detail": "Trp oxidation detected during cleavage. Crude purity < 60%. Not suitable for biological testing without significant re-optimization.",
"pipeline_version": "v0.5.75",
"source_batch": "wave0.5",
"score_activity": 0.68,
"score_safety": 0.75,
"score_novelty": 0.45,
"score_ensemble": 0.65,
"recalibration_used": "no"
},
{
"entry_id": 111,
"date": "2026-06-28",
"candidate_id": "DASH_EXAMPLE-011",
"sequence": "KLLKLLLKLLKLLL",
"reason_category": "selected_untested",
"reason_detail": "Candidate selected for Wave 0.5b but deferred pending safety assay optimisation. Expected to be tested in Wave 0.5c.",
"pipeline_version": "v0.5.76",
"source_batch": "wave0.5b",
"score_activity": 0.83,
"score_safety": 0.87,
"score_novelty": 0.35,
"score_ensemble": 0.75,
"recalibration_used": "no"
},
{
"entry_id": 112,
"date": "2026-07-01",
"candidate_id": "DASH_EXAMPLE-012",
"sequence": "RRWRRWRRWRRW",
"reason_category": "lab_inactive",
"reason_detail": "No bactericidal activity at 64 ug/mL against any of 5 tested strains. Time-kill assay confirmed static effect only at highest concentration.",
"pipeline_version": "v0.5.77",
"source_batch": "wave0.5b",
"assay_type": "MIC",
"assay_result": ">64",
"assay_unit": "ug/mL",
"score_activity": 0.80,
"score_safety": 0.52,
"score_novelty": 0.60,
"score_ensemble": 0.73,
"recalibration_used": "no"
},
{
"entry_id": 113,
"date": "2026-07-03",
"candidate_id": "DASH_EXAMPLE-013",
"sequence": "GIGAVILKVATGLPTLISWIK",
"reason_category": "pre_selection_reject",
"reason_detail": "Known toxic sequence pattern detected (melittin-like). Flagged by safety pre-filter.",
"pipeline_version": "v0.5.77",
"source_batch": "wave0.5b",
"score_activity": 0.87,
"score_safety": 0.15,
"score_novelty": 0.33,
"score_ensemble": 0.67,
"recalibration_used": "no"
},
{
"entry_id": 114,
"date": "2026-07-05",
"candidate_id": "DASH_EXAMPLE-014",
"sequence": "LLRLLRRLLRLLRR",
"reason_category": "control_failure",
"reason_detail": "Hemolysis control (Triton X-100) outside expected range. Assay batch flagged for QC review. Results not valid.",
"pipeline_version": "v0.5.78",
"source_batch": "wave0.5b",
"assay_type": "hemolysis_RBC",
"score_activity": 0.55,
"score_safety": 0.70,
"score_novelty": 0.30,
"score_ensemble": 0.52,
"recalibration_used": "no"
},
{
"entry_id": 115,
"date": "2026-07-07",
"candidate_id": "DASH_EXAMPLE-015",
"sequence": "RWKWRWKWRWKW",
"reason_category": "lab_inactive",
"reason_detail": "Biofilm inhibition < 25% at 128 ug/mL against S. aureus biofilm. Anti-biofilm assay showed no significant activity compared to untreated control.",
"pipeline_version": "v0.5.78",
"source_batch": "wave0.5b",
"assay_type": "biofilm_inhibition",
"assay_result": "<25",
"assay_unit": "% inhibition at 128 ug/mL",
"score_activity": 0.77,
"score_safety": 0.65,
"score_novelty": 0.55,
"score_ensemble": 0.71,
"recalibration_used": "no"
}
]
}
Loading
Loading