Files
breakpilot-compliance/backend-compliance/compliance/company/schemas.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

151 lines
5.9 KiB
Python

"""Company Intelligence (Phase 2A) — Company Capability Profile (domain objects).
This is the HEAD of the spine
Company -> (Operational) Capability -> Product -> Applicable Regulation ->
Obligation -> Procedure -> Evidence
and answers a DIFFERENT question than Regulatory Intelligence: not "which laws
apply to my product" but "which capabilities does my company already have, and
which regulatory obligations might they already cover".
HARD RULE (structural, not convention): a capability derived from a certification
is at most INFERRED — never CONFIRMED, never "erfuellt". A certification produces
EVIDENCE for a capability, an inference produces a CANDIDATE, and only checked
evidence produces a CONFIRMED capability. This keeps the company side inside
Welt 1 (potential), mirroring `ClaimCoverage` on the obligation side; it is NOT a
conformity verdict (`ComplianceStatus`, Welt 2, owned by Compliance Execution).
OWNERSHIP: Reasoning OWNS this CompanyContext container + the trust-state machine.
It does NOT own the Certification->Capability mapping RULES — those are the same
kind of rule as Feature->Capability and belong to the Compliance Execution
Capability Registry. This layer only CONSUMES `OperationalCapabilityCandidate`
{capability_id, source, confidence, verification_status} via an injected mapping
(see contract.py). No mapping DATA lives in product code (tests inject mocks).
Application/reasoning types, NOT compliance-meta-model classes (architecture
freeze v1.0 untouched). Python 3.9 compatible (no `|` unions).
"""
from __future__ import annotations
from enum import Enum
from typing import List, Optional
from pydantic import BaseModel, Field
from compliance.reasoning.enums import Confidence
class VerificationStatus(str, Enum):
"""Trust state of an operational capability — a FOURTH vocabulary.
Disjoint from ClaimCoverage (Welt 1, customer claim vs obligation),
ComplianceStatus (Welt 2, verified conformity) and DeltaType (RCI). It says
only how well-established a company CAPABILITY is, never whether an obligation
is met. Progression: DECLARED (customer says) -> INFERRED (a certification
implies it) -> CONFIRMED (checked against real evidence); UNKNOWN = no signal.
"""
DECLARED = "declared"
INFERRED = "inferred"
CONFIRMED = "confirmed"
UNKNOWN = "unknown"
# ── raw company inputs (the CompanyContext children) ──────────────────────
class Certification(BaseModel):
certification_id: str # e.g. "ISO27001"
name: str = ""
scope: str = "" # what the cert covers, customer-stated
class Declaration(BaseModel):
"""A customer statement that they have a capability ("we do patch management")."""
capability_id: str
statement: str = ""
class ExistingProcess(BaseModel):
process_id: str
name: str = ""
class ExistingSystem(BaseModel):
system_id: str
name: str = ""
class ExistingEvidence(BaseModel):
"""A concrete artefact the company already holds (policy, audit log, SBOM ...).
`proves_capability_id` is the ONLY thing that may lift a capability to
CONFIRMED — and only when a human/engine has attached real evidence.
"""
evidence_id: str
evidence_type: str = "" # config_export/test_report/policy/audit_log/...
proves_capability_id: Optional[str] = None
# ── intermediate: certification -> evidence-of-capability (refinement 1) ──
class CapabilityEvidence(BaseModel):
"""A certification does not yield a capability directly — only EVIDENCE for one.
"Company holds a certified ISMS" is the evidence/claim; capabilities are then
INFERRED from it via the injected (Execution-owned) mapping, never directly.
"""
source: str # provenance, e.g. "certification:ISO27001"
claim: str = ""
certification_id: str = ""
# ── consumed contract type (refinement 2) ─────────────────────────────────
class OperationalCapabilityCandidate(BaseModel):
"""The ONLY thing Reasoning consumes from Execution's capability mapping.
Named "operational" (organisational ability) to stay distinct from later
Product/AI/Safety capabilities. A candidate is always Welt 1 — DECLARED or
INFERRED — and never CONFIRMED on its own.
"""
capability_id: str
source: str
confidence: Confidence = Confidence.MEDIUM
verification_status: VerificationStatus = VerificationStatus.INFERRED
class OperationalCapability(BaseModel):
"""A capability the company actually has, CONFIRMED against real evidence."""
capability_id: str
verification_status: VerificationStatus
confidence: Confidence = Confidence.MEDIUM
sources: List[str] = Field(default_factory=list)
# ── the container Reasoning OWNS (raw inputs) ─────────────────────────────
class CompanyContext(BaseModel):
company_id: str
certifications: List[Certification] = Field(default_factory=list)
declarations: List[Declaration] = Field(default_factory=list)
processes: List[ExistingProcess] = Field(default_factory=list)
systems: List[ExistingSystem] = Field(default_factory=list)
evidence: List[ExistingEvidence] = Field(default_factory=list)
# ── derived view (the Company Capability Profile) ─────────────────────────
class CompanyCapabilityProfile(BaseModel):
"""Derived: capability evidence + candidates (declared/inferred) + confirmed.
`candidate_capabilities` NEVER auto-promote to `confirmed_capabilities`; only
explicit ExistingEvidence does that. The hard rule is enforced in engine.py.
"""
company_id: str
capability_evidence: List[CapabilityEvidence] = Field(default_factory=list)
candidate_capabilities: List[OperationalCapabilityCandidate] = Field(default_factory=list)
confirmed_capabilities: List[OperationalCapability] = Field(default_factory=list)