feat: Observation Model — the empirical learning unit, defined BEFORE persistence (Task 59a)
The learning point is not the hypothesis, it is the QUESTION — and confirmed/refuted is too coarse.
"partial, only critical suppliers" or "certified but not lived" are not "wrong", they are valuable
knowledge. So the chain is Hypothesis -> Question -> Observation -> (Review) -> Hypothesis, and the
observation model must be defined cleanly before any store/API (else thousands of too-coarse
observations get migrated later).
compliance/onboarding/observations.py:
- ObservationType: confirmed / partial / refuted / not_applicable / unknown (richer than binary).
- Observation: {hypothesis_id, capability, question, answer (free text), observation_type,
scope_note ("only critical suppliers"), evidence_uploaded, reviewed, reviewed_by}.
- empirical_distribution() -> a DISTRIBUTION (confirmed 61 / partial 31 / refuted 8), not one %.
- empirical_confidence() -> (confirmed + 0.5*partial) / (confirmed+partial+refuted); n.a./unknown
excluded; None until calibrated.
- REVIEW GATE: only reviewed observations calibrate — a raw answer never changes a hypothesis (no
learning from outliers).
Refactor: the hypothesis is now PURE curated knowledge — the binary observations counter and any
confidence are removed from CapabilityHypothesis and the YAML; confidence is COMPUTED from the separate
reviewed observation stream. Pure, mypy --strict clean. Persistence/aggregation/calibration are 59b/c/d.
Non-runtime -> no deploy. 12 tests pass, check-loc 0.
This commit is contained in:
@@ -11,17 +11,15 @@ long-term moat. The library is DATA, loaded outside this module and injected. Py
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Dict, List, Optional, Sequence
|
||||
from typing import Dict, List, Sequence
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class HypothesisObservations(BaseModel):
|
||||
confirmed: int = 0
|
||||
refuted: int = 0
|
||||
|
||||
|
||||
class CapabilityHypothesis(BaseModel):
|
||||
"""Curated knowledge only. Confidence is NOT stored here — it is computed from the reviewed
|
||||
observation stream (see observations.py); a raw answer never changes a hypothesis (review gate)."""
|
||||
|
||||
id: str
|
||||
capability: str
|
||||
supported_by: List[str] = Field(default_factory=list) # certifications that suggest this capability
|
||||
@@ -29,24 +27,9 @@ class CapabilityHypothesis(BaseModel):
|
||||
verification_required: bool = True # Welt-1: never auto-satisfied
|
||||
question_intent: str = "verify_existence"
|
||||
expected_evidence: List[str] = Field(default_factory=list)
|
||||
observations: HypothesisObservations = Field(default_factory=HypothesisObservations)
|
||||
kind: str = "shared" # shared / specific
|
||||
|
||||
|
||||
def empirical_confidence(obs: HypothesisObservations) -> Optional[float]:
|
||||
"""Confidence from observations only: confirmed / (confirmed+refuted). None until any are recorded."""
|
||||
n = obs.confirmed + obs.refuted
|
||||
return round(obs.confirmed / n, 2) if n else None
|
||||
|
||||
|
||||
def record_observation(obs: HypothesisObservations, confirmed: bool) -> HypothesisObservations:
|
||||
"""One real-onboarding observation -> updated counts (the empirical calibration step)."""
|
||||
return HypothesisObservations(
|
||||
confirmed=obs.confirmed + (1 if confirmed else 0),
|
||||
refuted=obs.refuted + (0 if confirmed else 1),
|
||||
)
|
||||
|
||||
|
||||
def inferred_hypotheses(
|
||||
certifications: Sequence[str], library: Sequence[CapabilityHypothesis]
|
||||
) -> List[CapabilityHypothesis]:
|
||||
|
||||
Reference in New Issue
Block a user