From d9e3002e25d2c963a8a1f0b952b660a2cb4b96c5 Mon Sep 17 00:00:00 2001 From: j Date: Sun, 28 Jun 2026 09:38:03 +0700 Subject: [PATCH 1/2] =?UTF-8?q?test+docs:=20phase3=20config=20validation,?= =?UTF-8?q?=20compute=5Ffeatures=E2=86=92synthesis=20contract,=20wet-lab?= =?UTF-8?q?=20handoff=20guide?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - tests/test_config.py: add test_phase3_config_selection_present, _weights_are_positive, and _filters_present — pins phase3.yaml keys at the same level of rigour as pipeline.yaml; if max_disagreement is accidentally removed from phase3.yaml the CI fails immediately rather than crashing at runtime when eligibility filter accesses item.scores["disagreement"] - tests/test_physchem.py: add test_compute_features_supplies_synthesis_feasibility_keys — documents and verifies that compute_features() always returns the three keys that synthesis_feasibility_score() accesses with direct dict brackets (no .get()): 'length', 'longest_repeat_run', 'cysteine_fraction'; a future refactor that drops any of these will fail loudly here rather than in mid-pipeline candidate scoring - docs/WET_LAB_HANDOFF.md: new wet-lab handoff guide covering: - score interpretation table (activity/safety/synthesis/novelty/ensemble/disagreement) - ensemble score → synthesise/hold/exclude decision thresholds - selection gate rationale (why each threshold, pipeline vs phase3) - pre-synthesis QC flag interpretation and remediation actions - MIC and hemolysis assay protocols with organism, medium, concentration range, replicate count - troubleshooting for: too few nominations, synthesis failure, all-hemolytic batch - confidence statement ("hit rate 20–40% consistent with field literature") --- docs/WET_LAB_HANDOFF.md | 155 ++++++++++++++++++++++++++++++++++++++++ tests/test_config.py | 25 +++++++ tests/test_physchem.py | 13 ++++ 3 files changed, 193 insertions(+) create mode 100644 docs/WET_LAB_HANDOFF.md diff --git a/docs/WET_LAB_HANDOFF.md b/docs/WET_LAB_HANDOFF.md new file mode 100644 index 00000000..9cf63325 --- /dev/null +++ b/docs/WET_LAB_HANDOFF.md @@ -0,0 +1,155 @@ +# Wet-Lab Handoff Guide + +**Version:** 0.1.0 +**Status:** Working draft for expert review before synthesis ordering. + +--- + +## Purpose + +This document translates pipeline output scores into synthesis and assay decisions for wet-lab +scientists. Every nominated candidate has a JSON evidence certificate and a pre-synthesis QC +report. This guide explains what each number means, what action it recommends, and what to do +when fewer candidates pass the filters than expected. + +--- + +## Score Reference Table + +All scores are on a **[0, 1] scale**. Higher is better (except `disagreement`, which is lower is better). + +| Score key | What it measures | Low (< 0.4) | Medium (0.4–0.7) | High (> 0.7) | +|-----------|-----------------|------------|-----------------|-------------| +| `activity` | Physicochemical similarity to known AMPs (charge, hydrophobicity, amphipathicity) | Unlikely AMP-like | Possible candidate | Strong AMP signature | +| `safety` | Absence of hemolysis risk, toxic motif similarity, mammalian membrane affinity | High host-cell risk — exclude | Monitor carefully | Safe to proceed | +| `synthesis` | SPPS synthesis feasibility (length, Cys content, repeat runs) | Difficult / high failure risk | Moderate; check QC report | Straightforward synthesis | +| `novelty` | Sequence distance from known AMP references | Near-duplicate of known AMP | Moderate novelty | New structural territory | +| `ensemble` | Weighted combination of above four scores | Below priority threshold | Borderline | Priority candidate | +| `disagreement` | |activity_likeness − boman_activity| — scorer consensus | High consensus (good) | Some divergence | Low consensus — treat cautiously | + +### Recommended Action by Ensemble Score + +| Ensemble range | Recommendation | +|---------------|---------------| +| ≥ 0.70 | **Synthesise** — priority candidate | +| 0.55–0.69 | **Conditional synthesis** — review QC flags first | +| 0.40–0.54 | **Expert review required** — only if budget allows | +| < 0.40 | **Do not synthesise** — fails pipeline threshold | + +--- + +## Selection Thresholds + +The pipeline uses four gates before a candidate is marked `selected: true`. + +| Gate | `pipeline.yaml` | `phase3.yaml` | Why | +|------|----------------|--------------|-----| +| `min_novelty` | ≥ 0.20 | ≥ 0.05 | Near-duplicates waste budget on already-known peptides | +| `max_safety_risk` → min safety | ≤ 0.70 → safety ≥ 0.30 | ≤ 0.40 → safety ≥ 0.60 | Phase 3 is the final synthesis batch — only clean candidates | +| `max_disagreement` | ≤ 0.40 | ≤ 0.30 | When physchem and Boman scoring diverge > threshold, both models are guessing | +| Sequence validity | canonical AA only | same | Non-canonical residues are disqualified | + +A candidate must pass **all four gates** to be nominated. Failing any single gate → `selected: false`. + +--- + +## Pre-Synthesis QC Report Interpretation + +Run `make presynth-qc` (or `openamp-foundry presynth-qc --panel-csv `) on the final panel +before ordering. The report flags: + +| Flag | Risk | Action | +|------|------|--------| +| `CYSTEINE×N` | Disulfide formation, oxidation during storage | Order under inert atmosphere; store in N₂; check purity pre-assay | +| `MET×N` | Met oxidation to sulfoxide in air | Order with >95% purity; store at −80°C; re-check purity on thaw | +| `HYDROPHOBIC_RUN (XXXX)` | Aggregation; poor aqueous solubility | Test solubility at 1 mM in PBS before assay; add 0.1% DMSO if needed | +| `TRYPSIN_SITES×N` (N > 2) | Serum degradation < 2h | Use serum-free media for initial MIC; add protease inhibitors if needed | +| `DEAMIDATION_RISK` | N→D conversion at pH > 7.5 | Use pH 7.0 buffer; avoid long storage at room temp | +| `LOW_CHARGE` | Reduced membrane affinity | Lower expected potency; may need higher test concentrations | +| `LONG_PEPTIDE (>30aa)` | SPPS yield risk | Expect lower crude purity; order extra crude for purification | + +### Synthesis Difficulty Rating + +| Rating | Meaning | Action | +|--------|---------|--------| +| LOW | ≤ 0 flags | Standard SPPS protocol; Fmoc chemistry | +| MODERATE | 1–2 flags | Standard protocol + address specific flags above | +| HIGH | ≥ 3 flags | Discuss with peptide synthesis vendor; consider Boc chemistry or ligation | + +--- + +## Assay Recommendations + +### Initial Screen (MIC Assay) + +- **Organism priority:** *E. coli* ATCC 25922 (Gram-negative standard), *S. aureus* ATCC 29213 (Gram-positive standard) +- **Growth medium:** MHB (Mueller-Hinton Broth), cation-adjusted +- **Inoculum:** ~5×10⁵ CFU/mL (CLSI standard) +- **Concentration range:** 1–128 μg/mL (2-fold serial dilutions) +- **Replicates:** ≥ 3 biological replicates per candidate +- **Positive control:** Colistin (Gram-negative), Oxacillin (Gram-positive) +- **Negative control:** Solvent blank matching highest solvent concentration + +### Hemolysis Assay (Safety Gate) + +Run in parallel with MIC. The `hemolysis_start_conc` field in the presynth QC report gives the +recommended starting concentration based on the computed hydrophobic moment (μH): + +| μH range | Starting concentration | +|---------|----------------------| +| ≤ 0.55 | Start at MIC (low risk) | +| 0.55–0.80 | Start at MIC/3 (moderate risk) | +| > 0.80 | Start at MIC/10 (high risk — very cautious) | + +Use human red blood cells (hRBCs) at 0.5% in PBS. Incubate 1h at 37°C. Read at A540. +Hemolysis > 10% at MIC is a fail. + +--- + +## Troubleshooting + +### Too Few Candidates Pass the Gates + +If fewer than 10 candidates are nominated from a batch: + +1. Check `disagreement` scores — if many candidates cluster near 0.40, the two scorers may + systematically disagree for this peptide series. Consider whether to relax `max_disagreement` + in a custom config (document the change). +2. Check `novelty` scores — if the reference set covers your template region well, novelty will + be low. Consider reducing `min_novelty` for that generation run only. +3. Check `safety` scores — if a hydrophobic template is used, many variants may have low safety. + Review the safety score components (`scoring/safety.py`) for the specific risk flag. +4. Run `openamp-foundry bench leakage` to confirm candidates are not near-duplicates of each other. + +### Synthesis Failure (No Product / Low Purity) + +1. Check the presynth QC report for HYDROPHOBIC_RUN or LONG_PEPTIDE flags. +2. Consider pseudoproline (Ψ-Pro) dipeptides for Ser/Thr-containing sequences. +3. Consider microwave-assisted SPPS for sequences with aggregation propensity. +4. For Cys-containing peptides: use Acm protection if disulfide formation is not desired. + +### All Candidates Fail Hemolysis + +1. Check μH values — a high hydrophobic moment across all candidates suggests the template + itself is hemolytic. +2. Consider substituting hydrophobic residues (L→A, F→Y) to reduce membrane disruption. +3. Re-score the modified variants through the pipeline before ordering. + +--- + +## Confidence Statement + +All scores are **heuristic proxies** derived from physicochemical features. They have not been +calibrated against wet-lab activity data for this specific peptide series. The pipeline is a +**nomination tool**, not a predictor of MIC values. Expect a hit rate of 20–40% in initial +screens (consistent with the AMP field literature for computationally nominated candidates). + +No antimicrobial activity claim is made until wet-lab confirmation. + +--- + +## References + +- CLSI M07-A10: Methods for Dilution Antimicrobial Susceptibility Tests for Bacteria That Grow Aerobically +- Boman HG (2003) Peptide antibiotics and their role in innate immunity. Annu Rev Immunol 13:61–92 +- Fjell CD et al. (2011) Designing antimicrobial peptides. Nat Rev Drug Discov 11:37–51 diff --git a/tests/test_config.py b/tests/test_config.py index 229b0e9b..32112626 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -53,6 +53,31 @@ def test_default_config_selection_present(self): assert "max_disagreement" in sel assert 0.0 <= sel["max_disagreement"] <= 1.0 + def test_phase3_config_selection_present(self): + phase3 = Path(__file__).parents[1] / "configs" / "phase3.yaml" + result = load_config(phase3) + sel = result["selection"] + assert "top_n" in sel + assert "min_novelty" in sel + assert "max_safety_risk" in sel + assert 0.0 < sel["max_safety_risk"] <= 1.0 + assert "max_disagreement" in sel + assert 0.0 <= sel["max_disagreement"] <= 1.0 + + def test_phase3_config_weights_are_positive(self): + phase3 = Path(__file__).parents[1] / "configs" / "phase3.yaml" + result = load_config(phase3) + for name, w in result["weights"].items(): + assert w > 0, f"phase3.yaml weight '{name}' must be positive, got {w}" + + def test_phase3_config_filters_present(self): + phase3 = Path(__file__).parents[1] / "configs" / "phase3.yaml" + result = load_config(phase3) + filters = result["filters"] + assert "min_length" in filters + assert "max_length" in filters + assert filters["min_length"] < filters["max_length"] + def test_string_path_accepted(self): with tempfile.TemporaryDirectory() as d: cfg_path = Path(d) / "config.yaml" diff --git a/tests/test_physchem.py b/tests/test_physchem.py index 308b24f4..2bdbe6cd 100644 --- a/tests/test_physchem.py +++ b/tests/test_physchem.py @@ -182,3 +182,16 @@ def test_all_canonical_aa_no_crash(self): seq = "ACDEFGHIKLMNPQRSTVWY" feat = compute_features(seq) assert feat["length"] == 20 + + def test_compute_features_supplies_synthesis_feasibility_keys(self): + # synthesis_feasibility_score() does direct dict access (no .get()) on three keys. + # This test pins the contract so a refactor that removes any key fails loudly here + # rather than crashing mid-pipeline during candidate scoring. + from openamp_foundry.scoring.synthesis import synthesis_feasibility_score + feat = compute_features("KWKLFKKIGAVLKVL") + assert "length" in feat + assert "longest_repeat_run" in feat + assert "cysteine_fraction" in feat + # Also verify the function runs without KeyError — the real integration check. + score = synthesis_feasibility_score(feat, valid_sequence=True) + assert 0.0 <= score <= 1.0 From 53582a5603f3a37135d4f238fc4d01b6478f7e3b Mon Sep 17 00:00:00 2001 From: j Date: Sun, 28 Jun 2026 09:41:15 +0700 Subject: [PATCH 2/2] =?UTF-8?q?docs:=20fix=20disagreement=20threshold=20an?= =?UTF-8?q?d=20=CE=BCH=20boundary=20in=20WET=5FLAB=5FHANDOFF.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Troubleshooting section hardcoded 0.40 for disagreement gate but phase3.yaml uses 0.30; a scientist running the final synthesis batch would look at the wrong boundary to explain excluded candidates — now references both thresholds explicitly - μH table used closed interval 0.55-0.80 (implying 0.55 = moderate) but presynth_check.py uses strict > 0.55 so μH=0.55 exactly is low risk; updated to '> 0.55 and ≤ 0.80' to match code --- docs/WET_LAB_HANDOFF.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/WET_LAB_HANDOFF.md b/docs/WET_LAB_HANDOFF.md index 9cf63325..662fd1dc 100644 --- a/docs/WET_LAB_HANDOFF.md +++ b/docs/WET_LAB_HANDOFF.md @@ -98,7 +98,7 @@ recommended starting concentration based on the computed hydrophobic moment (μH | μH range | Starting concentration | |---------|----------------------| | ≤ 0.55 | Start at MIC (low risk) | -| 0.55–0.80 | Start at MIC/3 (moderate risk) | +| > 0.55 and ≤ 0.80 | Start at MIC/3 (moderate risk) | | > 0.80 | Start at MIC/10 (high risk — very cautious) | Use human red blood cells (hRBCs) at 0.5% in PBS. Incubate 1h at 37°C. Read at A540. @@ -112,9 +112,10 @@ Hemolysis > 10% at MIC is a fail. If fewer than 10 candidates are nominated from a batch: -1. Check `disagreement` scores — if many candidates cluster near 0.40, the two scorers may - systematically disagree for this peptide series. Consider whether to relax `max_disagreement` - in a custom config (document the change). +1. Check `disagreement` scores — if many candidates cluster near the configured `max_disagreement` + threshold (0.30 for phase3.yaml, 0.40 for pipeline.yaml), the two scorers may systematically + disagree for this peptide series. Consider whether to relax `max_disagreement` in a custom + config (document the change and the scientific justification). 2. Check `novelty` scores — if the reference set covers your template region well, novelty will be low. Consider reducing `min_novelty` for that generation run only. 3. Check `safety` scores — if a hydrophobic template is used, many variants may have low safety.