mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-18 04:29:27 +00:00
feat(learning-harness): one teacher rule across every learning path
Task 3.1. A teacher is a vendored pair, at a review tier a maintainer stands behind, on the train split, and scorable. Every learning path now asks the same module that one question, so the split cannot be enforced in one place and forgotten in another. A refusal is data rather than an exception: the path records which holdout pairs a candidate touched and moves on, so a leak is visible in the run instead of silently accepted. The exporter refuses to seed from a holdout pair, leave-one-out teaches from the train split while still scoring every pair, and the mechanism lever refuses a candidate a holdout pair taught, because such a shape can only recover the pair that produced it. Measured and committed: on the vibe-py holdout the lever's own number falls from seven detected and a Youden of +0.059 to five detected and -0.059 once holdout pairs stop teaching, and the candidate set shrinks from ten shapes to six. That gap is the inflation this rule removes. Two existing tests encoded the leak and now encode the truth. The documented export-to-improve flow had a single holdout pair teaching itself, and now needs a train pair to teach and a holdout pair to recover. The obligation export claimed the corpus seeds at least one obligation shape, which was true only because holdout pairs were teaching: every vibe-py absence pair that yields an obligation lesson sits on the holdout side, so the honest number today is zero. The derivation stays covered by fixtures; the corpus gap belongs to the absence re-harvest and to a maintainer pass over the splits. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01T7XD7x5KonMdmGRkp63EQG
This commit is contained in:
co-authored by
Claude Opus 5
parent
e4b9607b2d
commit
86ed54c2e4
@@ -20,7 +20,7 @@ from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
|
||||
from ..findings import StaticFinding
|
||||
from ..pairs import GATING_TIERS, PairCase, _materialize, _targets
|
||||
from ..pairs import PairCase, _materialize, _targets
|
||||
from .facts import FactLoadError, SemanticFacts, load_facts
|
||||
from .functions import named_function_ranges, spans_named
|
||||
from .mechanisms import Mechanism, MechanismStore, append_from_pair, corpus_mechanisms
|
||||
@@ -99,7 +99,10 @@ def evaluate_loo(cases: Sequence[PairCase], *, facts: SemanticFacts | None = Non
|
||||
skipped.append((case.name, "pointer_pair_not_cached" if not case.vendored else "excerpt_missing"))
|
||||
else:
|
||||
targets.append(case)
|
||||
lessons = {case.name: pair_lessons(case, loaded) for case in targets if case.review_tier in GATING_TIERS}
|
||||
from ..learning.split import is_teacher
|
||||
|
||||
# learning-harness Req 5.1: every pair is still scored, but only a train-split teacher may seed a store.
|
||||
lessons = {case.name: pair_lessons(case, loaded) for case in targets if is_teacher(case)}
|
||||
outcomes = [_hold_out(case, targets, lessons, loaded) for case in targets]
|
||||
return LooResult(
|
||||
outcomes=tuple(outcomes),
|
||||
|
||||
@@ -193,6 +193,10 @@ def _obligation_facts() -> ObligationFacts:
|
||||
|
||||
|
||||
def _skip_reason(case: PairCase) -> str | None:
|
||||
from ..learning.split import is_teacher
|
||||
|
||||
if not is_teacher(case) and case.review_tier in GATING_TIERS and case.vendored and not case.known_limit:
|
||||
return f"split:{case.split}" # learning-harness Req 5.1: only train-split, scorable pairs teach
|
||||
if case.review_tier not in GATING_TIERS:
|
||||
return f"tier:{case.review_tier}"
|
||||
if case.known_limit:
|
||||
|
||||
Reference in New Issue
Block a user