Files
breakpilot-compliance/backend-compliance/compliance/company/engine.py
T
Benjamin Admin 8c893ca783 feat(company): Company Intelligence 2A — Company Capability Profile foundation
HEAD of the spine Company->Capability->Product->Regulation->Obligation->Procedure
->Evidence. New compliance/company/ package: CompanyContext container + a four-state
trust model (declared/inferred/confirmed/unknown).

Hard rule (structural): a certification yields at most an INFERRED candidate and is
never auto-treated as CONFIRMED/"erfuellt". A certification produces evidence-of-
capability; only real ExistingEvidence promotes a capability to CONFIRMED.

Ownership: Reasoning owns the container + trust-state; the Certification->Capability
mapping is Execution's domain, consumed via an injected contract. No mapping data in
product code (tests inject mocks). No endpoint/UI/RAG/new regs/controls; no meta-model
classes (freeze v1.0 untouched). 8 tests; mypy --strict clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-26 14:59:42 +02:00

115 lines
4.3 KiB
Python

"""Company Intelligence engine (Phase 2A) — build the Company Capability Profile.
Deterministic, no LLM/RAG. Turns a raw CompanyContext into capability evidence,
candidates and (only via explicit verification) confirmed capabilities.
HARD RULE enforced here: a certification yields at most an INFERRED candidate; it
can NEVER produce a CONFIRMED capability on its own. Only real ExistingEvidence
(`proves_capability_id`) promotes a capability to CONFIRMED. Certifications without
a known mapping yield evidence-of-claim but NO inferred capability (the mapping is
Execution's data, injected — never hard-coded here).
Python 3.9 compatible (no `|` unions).
"""
from __future__ import annotations
from typing import Dict, List, Optional, Tuple
from compliance.reasoning.enums import Confidence
from .contract import EMPTY_MAPPING, CertificationCapabilityMap
from .schemas import (
CapabilityEvidence,
CompanyCapabilityProfile,
CompanyContext,
OperationalCapability,
OperationalCapabilityCandidate,
VerificationStatus,
)
def _declared(context: CompanyContext) -> List[OperationalCapabilityCandidate]:
out: List[OperationalCapabilityCandidate] = []
for d in context.declarations:
out.append(
OperationalCapabilityCandidate(
capability_id=d.capability_id,
source="declaration:%s" % context.company_id,
confidence=Confidence.MEDIUM,
verification_status=VerificationStatus.DECLARED,
)
)
return out
def _from_certifications(
context: CompanyContext, mapping: CertificationCapabilityMap
) -> Tuple[List[CapabilityEvidence], List[OperationalCapabilityCandidate]]:
# refinement 1: certification -> evidence-of-capability (claim) -> inferred candidate
evidence: List[CapabilityEvidence] = []
inferred: List[OperationalCapabilityCandidate] = []
for cert in context.certifications:
source = "certification:%s" % cert.certification_id
evidence.append(
CapabilityEvidence(
source=source,
claim="Company holds %s" % (cert.name or cert.certification_id),
certification_id=cert.certification_id,
)
)
entry = mapping.get(cert.certification_id)
if entry is None:
continue # no mapping known -> NO inferred capability (data is Execution's)
for cap_id in entry.capability_ids:
inferred.append(
OperationalCapabilityCandidate(
capability_id=cap_id,
source=source,
confidence=entry.confidence,
verification_status=VerificationStatus.INFERRED,
)
)
return evidence, inferred
def _confirmed_from_evidence(context: CompanyContext) -> List[OperationalCapability]:
proven: Dict[str, List[str]] = {}
for ev in context.evidence:
cap = ev.proves_capability_id
if not cap:
continue
proven.setdefault(cap, []).append(ev.evidence_id)
return [
OperationalCapability(
capability_id=cap,
verification_status=VerificationStatus.CONFIRMED,
confidence=Confidence.HIGH,
sources=sources,
)
for cap, sources in proven.items()
]
def build_company_profile(
context: CompanyContext, mapping: Optional[CertificationCapabilityMap] = None
) -> CompanyCapabilityProfile:
"""Build the Company Capability Profile from raw context + an injected mapping.
`mapping` defaults to EMPTY (no inferred candidates) so that the cert->capability
table can only ever come from the Compliance Execution domain.
"""
mapping = EMPTY_MAPPING if mapping is None else mapping
evidence, inferred = _from_certifications(context, mapping)
declared = _declared(context)
confirmed = _confirmed_from_evidence(context)
confirmed_ids = {oc.capability_id for oc in confirmed}
# a confirmed capability is no longer a mere candidate
candidates = [c for c in (declared + inferred) if c.capability_id not in confirmed_ids]
return CompanyCapabilityProfile(
company_id=context.company_id,
capability_evidence=evidence,
candidate_capabilities=candidates,
confirmed_capabilities=confirmed,
)