Skip to content

Commit fa18d22

Browse files
cschanhniemOpenCode
andauthored
feat: Phase X X4 recalibration confidence certificate schema (RCC-) -- machine-checkable A/B/C/D grade for calibration weight reliability based on cohort size and cross-batch consistency (#852) (#1011)
VALID_RCC_GRADES (A-D), VALID_RCC_VERDICTS (4: high_confidence/moderate_confidence/low_confidence/insufficient_data), VALID_CONSISTENCY_RATINGS (4: consistent/moderately_consistent/inconsistent/unknown). BatchCohortEntry helper; grade A (>=4 batches consistent), B (>=2 batches consistent/moderately_consistent), D (insufficient_data when <2 batches or inconsistent). cit_id and lpr_id prefix-validated; dry_lab_only=True enforced; 76 tests. BASELINE updated 10367->10443. Co-authored-by: OpenCode <opencode@example.com>
1 parent 2a1e6c9 commit fa18d22

4 files changed

Lines changed: 674 additions & 2 deletions

File tree

‎docs/research/NEXT_100_PR_MAP.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,5 +291,5 @@ Track whether the pipeline actually improves across batches by capturing per-bat
291291
| X1 | Add multi-batch learning record schema (MBL-) (complete). — src/openamp_foundry/evidence/multi_batch_learning_record.py: VALID_MBL_QUALITY_GRADES (5: A-D/N/A), VALID_BATCH_LEARNING_STATUSES (3), GRADE_A_HIT_RATE=0.40; hit_rate auto-computed from n_confirmed_hits/n_candidates_tested; grade auto-derived; no_wet_lab_data forces N/A grade; whr_ids list; dry_lab_only=True; 61 tests in tests/evidence/test_multi_batch_learning_record.py. | Per-batch snapshot of prediction quality (hit rate, AUROC, n_confirmed_hits) after wet-lab feedback; enables cross-batch comparison of whether the pipeline is learning; feeds into calibration improvement tracker. | C |
292292
| X2 | Add calibration improvement tracker schema (CIT-) (complete). — src/openamp_foundry/evidence/calibration_improvement_tracker.py: VALID_CIT_TREND_DIRECTIONS (4: improving/stable/degrading/insufficient_data), VALID_CIT_SUMMARY_GRADES (5: A-D/N/A), MIN_BATCHES_FOR_TREND=2, IMPROVEMENT_THRESHOLD=0.05; BatchHitRateEntry helper; trend auto-computed from first→latest hit_rate delta; insufficient_data forces N/A grade; dry_lab_only=True; 49 tests. | Aggregates MBL records across batches; computes hit-rate trend direction (improving/stable/degrading/insufficient_data); minimum 2 batches required; flags when calibration is not producing measurable improvement. | C |
293293
| X3 | Add learning progress report schema (LPR-) (complete). — src/openamp_foundry/evidence/learning_progress_report.py: VALID_LPR_VERDICTS (4: learning_confirmed/learning_inconclusive/no_learning_signal/insufficient_data), VALID_FEATURE_PREDICTIVITY (3: predictive/not_predictive/uncertain), VALID_FEATURE_CATEGORIES (8); FeatureLearningEntry helper; verdict auto-computed: learning_confirmed (n_pred>n_non), learning_inconclusive (equal+>0), no_learning_signal (n_pred<n_non), insufficient_data (no batches or no definitive features); dry_lab_only=True; 58 tests in tests/evidence/test_learning_progress_report.py. | Human-readable summary of what the pipeline has learned from all batches to date; references CIT- for trend data; includes which candidate features proved predictive vs not; links to calibration decision logs. | C |
294-
| X4 | Add recalibration confidence certificate schema (RCC-). | Asserts with what confidence the current calibration weights are reliable based on cohort size, quality, and consistency across batches; A/B/C/D grade; prevents overconfident calibration claims. | C |
294+
| X4 | Add recalibration confidence certificate schema (RCC-) (complete). — src/openamp_foundry/evidence/recalibration_confidence_certificate.py: VALID_RCC_GRADES (A-D), VALID_RCC_VERDICTS (4: high_confidence/moderate_confidence/low_confidence/insufficient_data), VALID_CONSISTENCY_RATINGS (4); BatchCohortEntry helper; grade A (≥4 batches+consistent), B (≥2 batches+consistent/moderately_consistent), D (insufficient_data); cross_batch_consistency auto-computed; total_cohort_size auto-summed; dry_lab_only=True; cit_id/lpr_id prefix-validated; 76 tests. | Asserts with what confidence the current calibration weights are reliable based on cohort size, quality, and consistency across batches; A/B/C/D grade; prevents overconfident calibration claims. | C |
295295
| X5 | Add Phase X learning gate (XLG-). | Top-level gate asserting MBL + CIT + LPR + RCC all present; overall verdict: learning_verified/learning_in_progress/learning_not_started; closes Phase X; no calibration improvement claim is credible without passing this gate. | C |
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
"""RCC- recalibration confidence certificate schema.
2+
3+
Asserts with what confidence the current calibration weights are reliable,
4+
based on cohort size, quality consistency, and cross-batch agreement.
5+
Prevents overconfident calibration claims when evidence is thin.
6+
Gives a machine-checkable grade (A-D) that gates downstream claims.
7+
"""
8+
9+
from __future__ import annotations
10+
11+
from dataclasses import dataclass
12+
13+
VALID_RCC_GRADES: frozenset[str] = frozenset({
14+
"A",
15+
"B",
16+
"C",
17+
"D",
18+
})
19+
20+
VALID_RCC_VERDICTS: frozenset[str] = frozenset({
21+
"high_confidence",
22+
"moderate_confidence",
23+
"low_confidence",
24+
"insufficient_data",
25+
})
26+
27+
VALID_CONSISTENCY_RATINGS: frozenset[str] = frozenset({
28+
"consistent",
29+
"moderately_consistent",
30+
"inconsistent",
31+
"unknown",
32+
})
33+
34+
MIN_BATCHES_FOR_CONFIDENCE: int = 2
35+
HIGH_CONFIDENCE_MIN_BATCHES: int = 4
36+
MODERATE_CONFIDENCE_MIN_BATCHES: int = 2
37+
MIN_COHORT_SIZE_PER_BATCH: int = 5
38+
39+
GRADE_A_MIN_BATCHES: int = 4
40+
GRADE_B_MIN_BATCHES: int = 2
41+
GRADE_C_MIN_BATCHES: int = 1
42+
43+
44+
@dataclass
45+
class BatchCohortEntry:
46+
batch_id: str
47+
mbl_id: str
48+
cohort_size: int
49+
quality_grade: str
50+
51+
52+
@dataclass
53+
class RecalibrationConfidenceCertificate:
54+
rcc_id: str
55+
pipeline_version: str
56+
cit_id: str
57+
lpr_id: str
58+
batch_cohort_entries: list[BatchCohortEntry]
59+
n_batches_assessed: int
60+
total_cohort_size: int
61+
cross_batch_consistency: str
62+
rcc_grade: str
63+
rcc_verdict: str
64+
confidence_rationale: str
65+
dry_lab_only: bool
66+
limitations: list[str]
67+
created_at: str
68+
69+
70+
def validate_recalibration_confidence_certificate(
71+
rcc: RecalibrationConfidenceCertificate,
72+
) -> None:
73+
if not rcc.rcc_id.startswith("RCC-"):
74+
raise ValueError(f"rcc_id must start with 'RCC-': {rcc.rcc_id!r}")
75+
if not rcc.pipeline_version:
76+
raise ValueError("pipeline_version must be non-empty")
77+
if not rcc.cit_id.startswith("CIT-"):
78+
raise ValueError(f"cit_id must start with 'CIT-': {rcc.cit_id!r}")
79+
if not rcc.lpr_id.startswith("LPR-"):
80+
raise ValueError(f"lpr_id must start with 'LPR-': {rcc.lpr_id!r}")
81+
for entry in rcc.batch_cohort_entries:
82+
if entry.cohort_size < 0:
83+
raise ValueError(
84+
f"cohort_size must be non-negative for batch {entry.batch_id!r}"
85+
)
86+
if rcc.n_batches_assessed != len(rcc.batch_cohort_entries):
87+
raise ValueError("n_batches_assessed must equal len(batch_cohort_entries)")
88+
expected_total = sum(e.cohort_size for e in rcc.batch_cohort_entries)
89+
if rcc.total_cohort_size != expected_total:
90+
raise ValueError(
91+
f"total_cohort_size mismatch: expected {expected_total}, got {rcc.total_cohort_size}"
92+
)
93+
if rcc.cross_batch_consistency not in VALID_CONSISTENCY_RATINGS:
94+
raise ValueError(
95+
f"cross_batch_consistency {rcc.cross_batch_consistency!r} not in VALID_CONSISTENCY_RATINGS"
96+
)
97+
if rcc.rcc_grade not in VALID_RCC_GRADES:
98+
raise ValueError(
99+
f"rcc_grade {rcc.rcc_grade!r} not in VALID_RCC_GRADES"
100+
)
101+
if rcc.rcc_verdict not in VALID_RCC_VERDICTS:
102+
raise ValueError(
103+
f"rcc_verdict {rcc.rcc_verdict!r} not in VALID_RCC_VERDICTS"
104+
)
105+
if not rcc.confidence_rationale:
106+
raise ValueError("confidence_rationale must be non-empty")
107+
if not rcc.dry_lab_only:
108+
raise ValueError("dry_lab_only must be True")
109+
if not rcc.limitations:
110+
raise ValueError("limitations must be non-empty")
111+
if not rcc.created_at:
112+
raise ValueError("created_at must be non-empty")
113+
114+
115+
def _compute_consistency(
116+
entries: list[BatchCohortEntry],
117+
) -> str:
118+
if len(entries) < MIN_BATCHES_FOR_CONFIDENCE:
119+
return "unknown"
120+
grades = [e.quality_grade for e in entries if e.quality_grade != "N/A"]
121+
if not grades:
122+
return "unknown"
123+
unique = set(grades)
124+
if len(unique) == 1:
125+
return "consistent"
126+
if len(unique) == 2:
127+
return "moderately_consistent"
128+
return "inconsistent"
129+
130+
131+
def _compute_grade_and_verdict(
132+
n_batches: int,
133+
consistency: str,
134+
total_cohort_size: int,
135+
) -> tuple[str, str]:
136+
if n_batches == 0:
137+
return "D", "insufficient_data"
138+
if n_batches < MIN_BATCHES_FOR_CONFIDENCE:
139+
return "D", "insufficient_data"
140+
if n_batches >= GRADE_A_MIN_BATCHES and consistency == "consistent":
141+
return "A", "high_confidence"
142+
if n_batches >= GRADE_B_MIN_BATCHES and consistency in ("consistent", "moderately_consistent"):
143+
return "B", "moderate_confidence"
144+
if n_batches >= GRADE_C_MIN_BATCHES and consistency != "inconsistent":
145+
return "C", "low_confidence"
146+
return "D", "low_confidence"
147+
148+
149+
def _build_rationale(
150+
n_batches: int,
151+
consistency: str,
152+
total_cohort_size: int,
153+
grade: str,
154+
) -> str:
155+
return (
156+
f"Grade {grade}: {n_batches} batch(es) assessed, "
157+
f"total cohort size {total_cohort_size}, "
158+
f"cross-batch consistency={consistency}."
159+
)
160+
161+
162+
def build_recalibration_confidence_certificate(
163+
*,
164+
rcc_id: str,
165+
pipeline_version: str,
166+
cit_id: str,
167+
lpr_id: str,
168+
batch_cohort_entry_dicts: list[dict],
169+
limitations: list[str],
170+
created_at: str,
171+
) -> RecalibrationConfidenceCertificate:
172+
"""Build a RecalibrationConfidenceCertificate.
173+
174+
batch_cohort_entry_dicts: list of dicts with keys:
175+
batch_id, mbl_id, cohort_size, quality_grade
176+
"""
177+
entries = [
178+
BatchCohortEntry(
179+
batch_id=d["batch_id"],
180+
mbl_id=d["mbl_id"],
181+
cohort_size=int(d["cohort_size"]),
182+
quality_grade=d["quality_grade"],
183+
)
184+
for d in batch_cohort_entry_dicts
185+
]
186+
total = sum(e.cohort_size for e in entries)
187+
consistency = _compute_consistency(entries)
188+
grade, verdict = _compute_grade_and_verdict(len(entries), consistency, total)
189+
rationale = _build_rationale(len(entries), consistency, total, grade)
190+
rcc = RecalibrationConfidenceCertificate(
191+
rcc_id=rcc_id,
192+
pipeline_version=pipeline_version,
193+
cit_id=cit_id,
194+
lpr_id=lpr_id,
195+
batch_cohort_entries=entries,
196+
n_batches_assessed=len(entries),
197+
total_cohort_size=total,
198+
cross_batch_consistency=consistency,
199+
rcc_grade=grade,
200+
rcc_verdict=verdict,
201+
confidence_rationale=rationale,
202+
dry_lab_only=True,
203+
limitations=limitations,
204+
created_at=created_at,
205+
)
206+
validate_recalibration_confidence_certificate(rcc)
207+
return rcc
208+
209+
210+
def format_recalibration_confidence_certificate(
211+
rcc: RecalibrationConfidenceCertificate,
212+
) -> str:
213+
lines = [
214+
f"Recalibration Confidence Certificate — {rcc.rcc_id}",
215+
f"Pipeline: {rcc.pipeline_version} | CIT: {rcc.cit_id} | LPR: {rcc.lpr_id}",
216+
f"Grade: {rcc.rcc_grade} | Verdict: {rcc.rcc_verdict}",
217+
f"Batches assessed: {rcc.n_batches_assessed} | "
218+
f"Total cohort: {rcc.total_cohort_size} | "
219+
f"Consistency: {rcc.cross_batch_consistency}",
220+
f"Rationale: {rcc.confidence_rationale}",
221+
]
222+
if rcc.batch_cohort_entries:
223+
lines.append("Batch cohorts:")
224+
for entry in rcc.batch_cohort_entries:
225+
lines.append(
226+
f" {entry.batch_id} ({entry.mbl_id}): "
227+
f"cohort={entry.cohort_size} grade={entry.quality_grade}"
228+
)
229+
lines.append(f"Created: {rcc.created_at}")
230+
lines.append(f"Limitations: {'; '.join(rcc.limitations)}")
231+
lines.append(f"dry_lab_only: {rcc.dry_lab_only}")
232+
return "\n".join(lines)

0 commit comments

Comments
 (0)