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
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,12 @@ validate-rejection-events:
--out-json outputs/rejection_events_validation.json \
--out-md outputs/rejection_events_validation.md

check-negative-archive-completeness:
PYTHONPATH=src $(PYTHON) scripts/check_negative_archive_completeness.py \
--input examples/negative_result_archive_example.json \
--out-json outputs/negative_archive_completeness_report.json \
--out-md outputs/negative_archive_completeness_report.md

negative-result-dashboard:
PYTHONPATH=src $(PYTHON) scripts/negative_result_dashboard.py \
--input examples/negative_result_dashboard_example.json \
Expand Down
4 changes: 3 additions & 1 deletion 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 F9 — negative-result dashboard — v0.5.79)
> **Last updated:** 2026-07-09 (Phase F F10 — negative-result archive completeness checker — v0.5.80)
> **New in v0.5.80:** Negative-result archive completeness checker (F10) — CLI (`scripts/check_negative_archive_completeness.py`) that reads a JSON archive of negative-result entries and checks each entry against completeness criteria: all required fields present, no duplicate candidate_ids, each entry has at least one content field (assay_result, score_safety, reviewer_notes, or reason_detail), date format valid YYYY-MM-DD, and optional intake_report_id references are well-formed. Outputs a structured completeness report as JSON and Markdown with summary, per-check results, and per-entry pass/fail. Schema (`schemas/negative_result_archive_completeness.schema.json`). Example file (`examples/negative_result_archive_example.json`) with 4 toy entries. 35 tests. 2919 total.
>
> **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.
Expand Down
51 changes: 51 additions & 0 deletions docs/research/ROADMAP.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,56 @@
# Roadmap

## v0.5.80 — Loop 80: Phase F F10 — Negative-Result Archive Completeness Checker ✓ (2026-07-09)

CLI that reads a JSON archive of negative-result entries and checks each entry
against completeness criteria: required fields, duplicate candidate_ids, content
field presence, date format validity, and intake_report_id format. Prevents
cherry-picking by ensuring incomplete or poorly documented entries are detected
before analysis or reporting.

Changes:
- `scripts/check_negative_archive_completeness.py` (F10) — Standalone CLI that
loads negative-result entries (list or dict with `entries` key), checks each
entry for: required fields present, no duplicate candidate_ids, at least one
content field (assay_result, score_safety, reviewer_notes, or reason_detail),
valid YYYY-MM-DD date format, and well-formed intake_report_id (INT-YYYY-NNN).
Produces structured JSON + Markdown report with per-check and per-entry pass/fail.
Exit 0 on all pass, 1 on any failure, 2 on input errors.
- `schemas/negative_result_archive_completeness.schema.json` (F10) — JSON Schema
Draft 2020-12 for the completeness report output. Validates report_metadata,
summary (total_entries, pass/fail count, pass_rate), 5 checks (required_fields,
duplicate_candidate_ids, has_content_fields, date_format,
intake_report_id_references) each with pass boolean and details array,
per_entry_results array, and _caveat.
- `examples/negative_result_archive_example.json` (F10) — Toy example with 4
entries (lab_inactive, lab_toxic, control_failure, synthesis_failure) across
4 pipeline versions, including one entry with intake_report_id reference.
Clearly marked EXAMPLE — NOT REAL DATA.
- `tests/evidence/test_negative_archive_completeness.py` — 35 tests covering:
valid entries pass all checks, missing required field, duplicate candidate_id,
missing content fields, invalid date format, invalid calendar date, invalid
intake_report_id format, valid intake_report_id, mixed good/bad entries, empty
string required field, reason_detail as content, report structure (5 keys),
per-entry results matching count, empty entry handling, missing file, markdown
sections (title, summary, check results, caveat, errors), example file loading
and round-trip, all load_entries error modes, CLI exit codes (0, 1, 2), and
JSON/Markdown output writing.
- `Makefile` — Added `check-negative-archive-completeness` target.
- `docs/evidence/METRICS_CURRENT.md` — v0.5.80 F10 changelog. Test count: 2919.
- `tests/test_test_count_regression.py` — baseline updated to 2919.

Honest boundaries:
- Checks structural and formatting criteria only — a PASS does not confirm
biological accuracy, pipeline correctness, or data authenticity.
- Missing content fields may reflect genuine data absence (e.g., unreviewed
entries) rather than record-keeping errors.
- The intake_report_id format check validates pattern, not referential
integrity — a well-formed ID may reference a non-existent intake report.
- Duplicate candidate_id detection flags structural duplicates; it cannot
distinguish accidental duplicates from intentional re-entry of the same
candidate under different conditions.
- All conclusions about entry quality require qualified human review.

## 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
Expand Down
56 changes: 56 additions & 0 deletions examples/negative_result_archive_example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"schema": "EXAMPLE — NOT REAL DATA. For demonstration only.",
"description": "Toy negative-result archive entries for completeness checker demonstration. All candidates, sequences, scores, and reasons are synthetic and must not be treated as real experimental results.",
"pipeline_version": "v0.5.80",
"entries": [
{
"entry_id": 1,
"date": "2026-06-01",
"candidate_id": "ARCHIVE-001",
"sequence": "RRIRIIRRIRIIRRI",
"reason_category": "lab_inactive",
"reason_detail": "MIC > 128 ug/mL against E. coli.",
"pipeline_version": "v0.5.70",
"source_batch": "wave0.5",
"assay_result": ">128",
"score_activity": 0.82,
"score_safety": 0.35,
"reviewer_notes": "Confirmed inactive across 3 replicates."
},
{
"entry_id": 2,
"date": "2026-06-03",
"candidate_id": "ARCHIVE-002",
"sequence": "KLAKLAKKLAKLAK",
"reason_category": "lab_toxic",
"reason_detail": "HC50 = 8 ug/mL. High hemolysis risk.",
"pipeline_version": "v0.5.71",
"source_batch": "wave0.5",
"score_safety": 0.30,
"score_activity": 0.90
},
{
"entry_id": 3,
"date": "2026-06-05",
"candidate_id": "ARCHIVE-003",
"sequence": "GIGKFLHSAKKWGKAFVGEIMNS",
"reason_category": "control_failure",
"reason_detail": "Positive control failed. MIC for polymyxin B exceeded expected range.",
"pipeline_version": "v0.5.72",
"source_batch": "wave0.5",
"intake_report_id": "INT-2026-001",
"score_activity": 0.75,
"score_safety": 0.88
},
{
"entry_id": 4,
"date": "2026-06-10",
"candidate_id": "ARCHIVE-004",
"sequence": "RWQWRWQWRWQWRW",
"reason_category": "synthesis_failure",
"reason_detail": "Crude purity 62%, below 80% threshold. Synthesis failed after 3 attempts.",
"pipeline_version": "v0.5.73",
"source_batch": "wave0.5"
}
]
}
90 changes: 90 additions & 0 deletions schemas/negative_result_archive_completeness.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Negative-Result Archive Completeness Report",
"description": "Schema for the structured completeness report produced by scripts/check_negative_archive_completeness.py.",
"type": "object",
"required": ["report_metadata", "summary", "checks", "per_entry_results", "_caveat"],
"properties": {
"report_metadata": {
"type": "object",
"required": ["generated_at", "pipeline_version", "source_file", "total_entries"],
"properties": {
"generated_at": {"type": "string", "format": "date-time"},
"pipeline_version": {"type": "string"},
"source_file": {"type": "string"},
"total_entries": {"type": "integer", "minimum": 0}
}
},
"summary": {
"type": "object",
"required": ["total_entries", "pass_count", "fail_count", "pass_rate"],
"properties": {
"total_entries": {"type": "integer", "minimum": 0},
"pass_count": {"type": "integer", "minimum": 0},
"fail_count": {"type": "integer", "minimum": 0},
"pass_rate": {"type": "string"}
}
},
"checks": {
"type": "object",
"required": ["required_fields", "duplicate_candidate_ids", "has_content_fields", "date_format", "intake_report_id_references"],
"properties": {
"required_fields": {
"type": "object",
"required": ["pass", "details"],
"properties": {
"pass": {"type": "boolean"},
"details": {"type": "array", "items": {"type": "string"}}
}
},
"duplicate_candidate_ids": {
"type": "object",
"required": ["pass", "details"],
"properties": {
"pass": {"type": "boolean"},
"details": {"type": "array", "items": {"type": "string"}}
}
},
"has_content_fields": {
"type": "object",
"required": ["pass", "details"],
"properties": {
"pass": {"type": "boolean"},
"details": {"type": "array", "items": {"type": "string"}}
}
},
"date_format": {
"type": "object",
"required": ["pass", "details"],
"properties": {
"pass": {"type": "boolean"},
"details": {"type": "array", "items": {"type": "string"}}
}
},
"intake_report_id_references": {
"type": "object",
"required": ["pass", "details"],
"properties": {
"pass": {"type": "boolean"},
"details": {"type": "array", "items": {"type": "string"}}
}
}
}
},
"per_entry_results": {
"type": "array",
"items": {
"type": "object",
"required": ["entry_id", "candidate_id", "checks", "errors", "pass"],
"properties": {
"entry_id": {"type": ["integer", "string"]},
"candidate_id": {"type": "string"},
"checks": {"type": "object"},
"errors": {"type": "array", "items": {"type": "string"}},
"pass": {"type": "boolean"}
}
}
},
"_caveat": {"type": "string"}
}
}
Loading
Loading