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>
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user