Skip to content

Commit 2cd7137

Browse files
committed
spec R39: pin VHH thresholds from TNP source assign_flag()
Spec R39 said VHH PSH/PPC/PNC/totalCdrLength thresholds were "pinned at M1 from TNP source `bin_*` functions". The values live in oxpig/TNP `bin/TNP`'s `assign_flag()` function (not `bin_*`, but the function the spec refers to). Transcribed verbatim with cohortSize 36: totalCdrLength: bidirectional, green 25-36, amber 20-24 OR 37-39, red <20 OR >39 psh: bidirectional, green 79.60-126.82, amber 73.40-79.59 OR 126.83-155.47, red <73.40 OR >155.47 ppc: high_bad, amber 0.39-1.18, red >1.18 pnc: high_bad, amber 1.47-1.88, red >1.88 cdrh3Compactness: bidirectional, green 0.82-1.56, amber 0.56-0.81 OR 1.57-1.61, red <0.56 OR >1.61 `scoring.py` was falling back to Fv values for the four shared metrics when mode == TNP. Now uses the VHH dict end-to-end. Citation in the JSON points to the TNP repo + assign_flag() function name so the source is traceable. Minor edge-case correction to the cdrh3Compactness bands: spec text listed green 0.82-1.57 but TNP source has amber starting at 1.57 (green effectively 0.82-1.56). Source-authoritative.
1 parent f8ee498 commit 2cd7137

2 files changed

Lines changed: 34 additions & 15 deletions

File tree

software/liabilities-script/data/thresholds.json

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"_comment": "Spec R39 amber/red threshold bands per Raybould 2019 Table 2 (Fv mode) and Gordon 2025 (VHH mode CDRH3 compactness). cohortSize is the calibration-cohort size from the source paper; preserved here so downstream users can weigh the threshold's statistical confidence. VHH PSH/PPC/PNC/totalCdrLength thresholds are deliberately omitted — they need M1 calibration from the TNP source paper; the scoring code falls back to Fv values until then.",
2+
"_comment": "Spec R39 amber/red threshold bands. Fv mode: Raybould 2019 Table 2 (cohortSize 242). VHH mode: Gordon 2025 TNP `assign_flag()` function in oxpig/TNP repo (cohortSize 36). cohortSize preserved so downstream users can weigh statistical confidence.",
33
"fv": {
44
"cohortSize": 242,
55
"citation": "Raybould et al., PNAS 2019, vol. 116 no. 10, 4025-4030 (Table 2)",
@@ -30,12 +30,32 @@
3030
},
3131
"vhh": {
3232
"cohortSize": 36,
33-
"citation": "Gordon et al., bioRxiv 2025, doi:10.1101/2025.08.11.669635 (TNP)",
33+
"citation": "Gordon et al., bioRxiv 2025, doi:10.1101/2025.08.11.669635 (TNP); thresholds verbatim from oxpig/TNP `bin/TNP` assign_flag()",
3434
"thresholds": {
35+
"totalCdrLength": {
36+
"bidirectional": true,
37+
"green": [25, 36],
38+
"amber_lo": [20, 24],
39+
"amber_hi": [37, 39]
40+
},
41+
"psh": {
42+
"bidirectional": true,
43+
"green": [79.60, 126.82],
44+
"amber_lo": [73.40, 79.59],
45+
"amber_hi": [126.83, 155.47]
46+
},
47+
"ppc": {
48+
"direction": "high_bad",
49+
"amber": [0.39, 1.18]
50+
},
51+
"pnc": {
52+
"direction": "high_bad",
53+
"amber": [1.47, 1.88]
54+
},
3555
"cdrh3Compactness": {
3656
"bidirectional": true,
37-
"green": [0.82, 1.57],
38-
"amber_lo": [0.56, 0.82],
57+
"green": [0.82, 1.56],
58+
"amber_lo": [0.56, 0.81],
3959
"amber_hi": [1.57, 1.61]
4060
}
4161
}

software/liabilities-script/scoring.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@
33
Thresholds live in `data/thresholds.json` per spec line 142 (`R39`) so the
44
calibration source + cohortSize travel with the values. Loaded once at
55
import time; runtime threshold checks read from the parsed dict.
6-
Composite scoring (R41) mirrors `compute_developability_score` in the
7-
sequence-liabilities block: fixability_weight × region_weight × exposure
8-
for motifs, plus per-mode flag bumps (red=8, amber=3, green=0), plus the
9-
Cys contributions (8 × exposed_extra + 20 × broken_canonical + 20 ×
10-
missing_canonical).
11-
12-
VHH PSH/PPC/PNC/totalCdrLength thresholds are deliberately omitted from
13-
the JSON — they need M1 calibration against the TNP source paper; the
14-
code below falls back to Fv values for those four metrics until then.
6+
Fv values come from Raybould 2019 Table 2 (cohortSize 242). VHH values
7+
come from the TNP source paper's `assign_flag()` function in
8+
oxpig/TNP `bin/TNP` (cohortSize 36). Composite scoring (R41) mirrors
9+
`compute_developability_score` in the sequence-liabilities block:
10+
fixability_weight × region_weight × exposure for motifs, plus per-mode
11+
flag bumps (red=8, amber=3, green=0), plus the Cys contributions
12+
(8 × exposed_extra + 20 × broken_canonical + 20 × missing_canonical).
1513
"""
1614

1715
import json
@@ -103,8 +101,9 @@ def compute_flags(surface_metrics: dict) -> dict[str, str]:
103101
if mode == "TAP":
104102
thresholds = _FV_THRESHOLDS
105103
elif mode == "TNP":
106-
# Fall back to Fv thresholds for the four shared metrics; add VHH-specific.
107-
thresholds = {**_FV_THRESHOLDS, **_VHH_THRESHOLDS}
104+
# VHH is now fully populated from TNP source `assign_flag()`; use
105+
# its own thresholds end-to-end (no Fv fallback needed).
106+
thresholds = _VHH_THRESHOLDS
108107
else:
109108
return {}
110109

0 commit comments

Comments
 (0)