8c893ca783
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>
151 lines
5.9 KiB
Python
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)
|