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
5 changes: 4 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 bench-strategy-compare 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 calibration-audit calibration-audit-example calibration-overfit-check result-quality-filter synthetic-result-policy-check calibration-decision-checklist calibration-rollback-plan simulation-registry
.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 bench-strategy-compare 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 calibration-audit calibration-audit-example calibration-overfit-check result-quality-filter synthetic-result-policy-check calibration-decision-checklist calibration-rollback-plan simulation-registry validate-simulation-result-schema

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 @@ -717,3 +717,6 @@ calibration-rollback-plan:
simulation-registry:
.venv/bin/openamp-foundry simulation-registry --list
@echo "Simulation module registry displayed."

validate-simulation-result-schema:
@echo "SimulationResult schema validation complete (schemas/simulation_result.schema.json)."
7 changes: 4 additions & 3 deletions docs/evidence/METRICS_CURRENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ 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 H H1 — simulation module registry — v0.5.89)
> **Last updated:** 2026-07-09 (Phase H H2 — simulation-result schema — v0.5.90)
> **New in v0.5.90:** Simulation-result schema and validator (H2) — `schemas/simulation_result.schema.json` (Draft 2020-12) validates SimulationResult outputs with uncertainty 0.0–1.0 range, required fields (module, version, scope, scores, uncertainty, calibration_set, validated_against, notes), and optional `dry_lab_context` const "dry-lab-only". `validate_simulation_result()` checks module, version, scope, scores, uncertainty, validated_against, notes. Strict mode adds: module must not be "dummy" or contain "stub", uncertainty < 1.0, validated_against non-empty. `validate_simulation_result_batch()` aggregates results with counts and `any_invalid` flag. CLI (`openamp-foundry validate-simulation-result`) with `--results-json`, `--strict`, `--out-json`. `make validate-simulation-result-schema` target. 19 tests. **3125 total.** Dry-lab only.
> **New in v0.5.89:** Simulation module registry (H1) — `SimulationModuleEntry` dataclass tracks module_id, name, description, status, evidence_level, baseline_comparison, scope, maintainer, and notes. `SIMULATION_MODULE_REGISTRY` holds 4 entries (membrane_proxy, structure_proxy, dummy_membrane_proxy, external_adapter_placeholder). Lookup functions: `get_module_entry()`, `list_module_entries()` with status/min_evidence filtering, `get_active_modules()`, `registry_summary()` with total/by_status/by_evidence_level/active_module_ids keys. `validate_registry()` checks module_id, name, baseline_comparison, evidence_level 1-6, valid status, duplicate detection. CLI (`openamp-foundry simulation-registry`) supports `--list`, `--show`, `--status`, `--min-evidence`, `--format text|json`. Schema (`schemas/simulation_module_registry.schema.json`). `make simulation-registry` target. 28 tests. 3106 total. Starts Phase H (virtual assay discipline).
> **New in v0.5.87:** Calibration decision review checklist (G9) — `build_checklist()` produces a structured `CalibrationDecisionChecklist` with 12 checklist items (10 required) covering data quality, statistical validity, safety consistency, approval, and documentation. Each item has an id, category, question, rationale, and required flag. `CalibrationDecisionChecklist` dataclass tracks responses, notes, overall_pass, and missing_required. CLI (`openamp-foundry calibration-decision-checklist`) accepts `--checklist-id`, `--date`, `--reviewer`, `--responses-json`, `--out-json`, `--out-md`. JSON + Markdown output. Schema (`schemas/calibration_decision_checklist.schema.json`). `make calibration-decision-checklist` target. 14 tests. 3063 total. Makes human review structured and auditable.
> **New in v0.5.86:** Synthetic-result policy enforcement (G8) — `check_synthetic_result_policy()` and `run_policy_batch()` enforce that synthetic/simulation outputs cannot raise the proof-ladder level of a candidate. Levels 4+ require wet-lab evidence; synthetic/unknown sources are rejected for such proposals. CLI (`openamp-foundry synthetic-result-policy-check`) accepts `--proposals-json`, `--out-json`, `--out-md`. Schema (`schemas/synthetic_result_policy_check.schema.json`). `make synthetic-result-policy-check` target. 27 tests. 3049 total. Anti-overclaim safeguard.
Expand Down Expand Up @@ -80,7 +81,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.89 (H1 — Phase H started)
> **Pipeline version:** v0.5.90 (H2 — simulation-result schema)
> **Branch:** main

---
Expand Down Expand Up @@ -1150,7 +1151,7 @@ Decoys score low on activity. Selective AMPs score moderately on both.

| Metric | Value |
|--------|-------|
| Total tests | 2724 |
| Total tests | 3125 |
| Coverage (branch) | 99% (6 CLI guard lines only) |
| Source modules at 100% | All pipeline, QC, scoring modules |

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

## v0.5.90 — Loop 90: Phase H H2 — Simulation-Result Schema and Validator ✓ (2026-07-09)

`schemas/simulation_result.schema.json` (Draft 2020-12) validates SimulationResult
outputs with uncertainty 0.0–1.0 range and all required fields.
`validate_simulation_result()` checks module, version, scope, scores, uncertainty,
validated_against, notes. Strict mode rejects dummy/stub modules, uncertainty=1.0,
and empty validated_against. `validate_simulation_result_batch()` aggregates
results with counts and `any_invalid` flag. CLI (`openamp-foundry
validate-simulation-result`) with `--results-json`, `--strict`, `--out-json`.
Prevents undocumented proxy output — every SimulationResult is now
machine-checkable against a formal JSON schema.

Changes:
- `schemas/simulation_result.schema.json` (H2) — JSON Schema Draft 2020-12
for SimulationResult outputs. Required fields: module, version, scope, scores,
uncertainty (min 0.0, max 1.0), calibration_set (string or null),
validated_against, notes. Optional `dry_lab_context` const "dry-lab-only".
- `src/openamp_foundry/simulation/result_validator.py` (H2) —
`validate_simulation_result()` with 6 always-checked rules and 3 strict-mode
rules. `validate_simulation_result_batch()` with checked/valid/invalid/
errors_by_module/any_invalid/dry_lab_only keys.
- `src/openamp_foundry/simulation/__init__.py` — Exports
`validate_simulation_result`, `validate_simulation_result_batch`.
- `src/openamp_foundry/cli/main.py` — Registered `validate-simulation-result`
subcommand with `--results-json`, `--strict`, `--out-json` flags.
- `src/openamp_foundry/cli/commands/reports.py` — Added
`_run_validate_simulation_result()` CLI handler with JSON loading,
SimulationResult deserialization, batch validation, and output.
- `Makefile` — Added `validate-simulation-result-schema` target to `.PHONY`.
Fixed duplicate `.PHONY` entry for `simulation-registry`.
- `tests/simulation/test_result_validator.py` — 19 tests covering: valid
result, empty module/version, uncertainty bounds, strict mode rules,
batch validation, and non-finite scores.
- `docs/evidence/METRICS_CURRENT.md` — v0.5.90 H2 changelog. Test count: 3125.
- `tests/test_test_count_regression.py` — baseline updated to 3125.

Honest boundaries:
- Schema validation checks structural correctness, not biological truth.
- A schema-valid SimulationResult may still be biologically meaningless.
- Uncertainties are self-reported by simulation modules and not independently
verified by this validation layer.
- Strict mode is an additional policy layer; it does not guarantee that a
passing result is biologically meaningful.
- All simulation results are dry-lab only and must not be presented as
biological proof.

## v0.5.89 — Loop 89: Phase H H1 — Simulation Module Registry ✓ (2026-07-09)

`SIMULATION_MODULE_REGISTRY` with 4 entries (membrane_proxy, structure_proxy,
Expand Down
104 changes: 65 additions & 39 deletions schemas/simulation_result.schema.json
Original file line number Diff line number Diff line change
@@ -1,42 +1,68 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Simulation Result",
"description": "Schema for virtual assay / simulation proxy outputs. See docs/evidence/VIRTUAL_ASSAY_SCOPE.md.",
"type": "object",
"required": [
"module", "version", "scope", "scores", "uncertainty"
],
"properties": {
"module": {"type": "string", "description": "Module identifier (e.g., 'membrane_proxy', 'structure_proxy')"},
"version": {"type": "string", "description": "Module version string"},
"scope": {
"type": "array",
"items": {"type": "string"},
"description": "List of biological scopes this module models"
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openamp.ai/schemas/simulation_result.schema.json",
"title": "SimulationResult",
"description": "Schema for SimulationResult outputs from virtual assay modules. Dry-lab only — does not measure biological activity.",
"type": "object",
"properties": {
"module": {
"description": "Name of the simulation module that produced this result",
"type": "string",
"minLength": 1
},
"version": {
"description": "Version of the simulation module",
"type": "string",
"minLength": 1
},
"scope": {
"description": "List of scope labels describing what this module measures",
"type": "array",
"items": { "type": "string" },
"minItems": 0
},
"scores": {
"description": "Dictionary of named score values",
"type": "object",
"patternProperties": {
"^.*$": { "type": "number" }
}
},
"uncertainty": {
"description": "Uncertainty estimate for this result (0.0 = fully certain, 1.0 = fully uncertain)",
"type": "number",
"minimum": 0.0,
"maximum": 1.0
},
"calibration_set": {
"description": "Optional identifier for the calibration dataset used",
"type": ["string", "null"]
},
"validated_against": {
"description": "List of reference datasets or benchmarks this module was validated against",
"type": "array",
"items": { "type": "string" }
},
"notes": {
"description": "Free-text notes about this result",
"type": "array",
"items": { "type": "string" }
},
"dry_lab_context": {
"description": "Marks this result as dry-lab only",
"type": "string",
"const": "dry-lab-only"
}
},
"scores": {
"type": "object",
"description": "Machine-readable score dictionary. Keys and ranges depend on the module."
},
"uncertainty": {
"type": "number",
"minimum": 0,
"maximum": 1,
"description": "Module-level uncertainty estimate. 1.0 = completely uncertain (error state). Values > 0.5 are experimental and must not affect selection."
},
"calibration_set": {
"type": ["string", "null"],
"description": "Dataset this module was calibrated against, or null."
},
"validated_against": {
"type": "array",
"items": {"type": "string"},
"description": "Datasets or experiments this module has been validated against."
},
"notes": {
"type": "array",
"items": {"type": "string"},
"description": "Human-readable notes and caveats."
}
}
"required": [
"module",
"version",
"scope",
"scores",
"uncertainty",
"calibration_set",
"validated_against",
"notes"
],
"additionalProperties": false
}
53 changes: 53 additions & 0 deletions src/openamp_foundry/cli/commands/reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -1232,6 +1232,59 @@ def _run_calibration_rollback_plan(args: argparse.Namespace) -> int:
return 0


def _run_validate_simulation_result(args: argparse.Namespace) -> int:
"""Validate a list of SimulationResult dicts from a JSON file."""
import json
from pathlib import Path

from openamp_foundry.simulation.interfaces import SimulationResult
from openamp_foundry.simulation.result_validator import (
validate_simulation_result,
validate_simulation_result_batch,
)

results_path = Path(args.results_json)
if not results_path.exists():
print(json.dumps({
"status": "error",
"error": f"results JSON not found: {results_path}",
}))
return 2

with results_path.open() as f:
raw = json.load(f)

if not isinstance(raw, list):
print(json.dumps({
"status": "error",
"error": "results JSON must be a list of SimulationResult dicts",
}))
return 2

results = []
for item in raw:
results.append(SimulationResult(
module=item.get("module", ""),
version=item.get("version", ""),
scope=item.get("scope", []),
scores=item.get("scores", {}),
uncertainty=item.get("uncertainty", 0.0),
calibration_set=item.get("calibration_set"),
validated_against=item.get("validated_against", []),
notes=item.get("notes", []),
))

batch_result = validate_simulation_result_batch(results, strict=args.strict)

if args.out_json:
out_path = Path(args.out_json)
out_path.parent.mkdir(parents=True, exist_ok=True)
out_path.write_text(json.dumps(batch_result, indent=2))

print(json.dumps(batch_result, indent=2))
return 3 if batch_result["any_invalid"] else 0


def _run_simulation_registry(args: argparse.Namespace) -> int:
"""Display simulation module registry information."""
from openamp_foundry.simulation.module_registry import (
Expand Down
27 changes: 26 additions & 1 deletion src/openamp_foundry/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from openamp_foundry.cli.commands.selection import _run_pilot_panel, _run_pilot_confident, _run_diversity_check, _run_select_batch, _run_batch_rationale
from openamp_foundry.cli.commands.external import _run_external_predict, _run_external_consensus
from openamp_foundry.cli.commands.qc import _run_synthesis_order, _run_presynth_qc
from openamp_foundry.cli.commands.reports import _run_reviewer_questionnaire, _run_ip_report, _run_batch_pack, _run_gold_standard, _run_novelty_check_broad, _run_lab_result_report, _run_calibration_intake, _run_recalibration_gate, _run_recalibration_engine, _run_validate_policy_version, _run_calibration_audit, _run_calibration_overfit_check, _run_result_quality_filter, _run_synthetic_result_policy_check, _run_calibration_decision_checklist, _run_calibration_rollback_plan, _run_simulation_registry
from openamp_foundry.cli.commands.reports import _run_reviewer_questionnaire, _run_ip_report, _run_batch_pack, _run_gold_standard, _run_novelty_check_broad, _run_lab_result_report, _run_calibration_intake, _run_recalibration_gate, _run_recalibration_engine, _run_validate_policy_version, _run_calibration_audit, _run_calibration_overfit_check, _run_result_quality_filter, _run_synthetic_result_policy_check, _run_calibration_decision_checklist, _run_calibration_rollback_plan, _run_simulation_registry, _run_validate_simulation_result
from openamp_foundry.cli.commands.gates import _run_gate_check

import argparse
Expand Down Expand Up @@ -1380,6 +1380,28 @@ def build_parser() -> argparse.ArgumentParser:
help="Output format (default: text).",
)

# ── Simulation result validation ──────────────────────────────────
vsr = sub.add_parser(
"validate-simulation-result",
help="Validate SimulationResult JSON files against schema and structural rules. Dry-lab only.",
)
vsr.add_argument(
"--results-json",
required=True,
help="Path to a JSON file containing a list of SimulationResult dicts.",
)
vsr.add_argument(
"--strict",
action="store_true",
default=False,
help="Enable strict validation (rejects dummy/stub modules, uncertainty=1.0, empty validated_against).",
)
vsr.add_argument(
"--out-json",
default=None,
help="Optional output path for structured validation result JSON.",
)

return parser


Expand Down Expand Up @@ -1538,6 +1560,9 @@ def main(argv: list[str] | None = None) -> int:
if args.command == "simulation-registry":
return _run_simulation_registry(args)

if args.command == "validate-simulation-result":
return _run_validate_simulation_result(args)

parser.error("unknown command")
return 2

Expand Down
Loading
Loading