80bf1993e0
The sanctioned last architectural building block. Reverses the order: not Goal -> Journey -> Delta but Goal -> Required -> Delta -> Journey. A Journey is the EXPLANATION of the Capability Delta, not its cause — so this is a Matcher/Explainer, not a Selector. New module compliance/journey_matcher/ = the third independent, interchangeable function of the pipeline, beside Company 2A (Evidence -> Capability) and RS-005 (Capability -> Delta): match_journeys(delta, journeys, context) -> ranked, auditable explanation - Looks ONLY at the Capability Delta — never at certificates, regulation, tenders or the goal. Journey signatures are certificate-agnostic capability clusters (Input -> Output pattern). - score = share of the delta a journey explains (recall over the missing capabilities); journey_only documents where a journey reaches beyond the delta so a broad journey is not silently preferred. - Deliberately dumb + deterministic (pure set overlap; NO ML/embeddings/LLM), fully auditable (matched / unexplained / journey_only / context signals); a learning ranker can sit on top later. - Signatures injected, engine hermetic. mypy --strict clean. Validated on the real patterns (demo): a CRA+MaschinenVO delta ranks the convergence journey 100%, "ISO27001 -> CRA" 56% (misses the machine-safety caps), "ISMS -> TISAX" 0%. This resolves the "Scope -> Journey" jump from Customer Mission #1. Freeze exception explicitly authorised; non-runtime -> no deploy. 12 tests pass, check-loc 0.
67 lines
2.9 KiB
Python
67 lines
2.9 KiB
Python
"""Schemas for the Journey Matcher — the Delta -> Journey function of the Capability Delta Engine.
|
|
|
|
Derived views (computed-not-stored): nothing here is persisted; every match is recomputed from the
|
|
input delta + injected journey signatures each call. No new corpus, no graph (freeze v1.0).
|
|
Python 3.9 compatible (no `|` unions).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import List, Optional
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
class JourneySignature(BaseModel):
|
|
"""A known journey described ONLY by its capability pattern (Input cluster -> Output cluster).
|
|
|
|
Deliberately certificate-/regulation-agnostic: the match uses `capability_pattern` alone. `label`
|
|
and the context fields exist for the human-auditable explanation, NEVER for the score. (Today the
|
|
signatures are derived from the transition patterns; the IDs like "ISO27001->CRA" are just one way
|
|
to describe the clusters — the matcher never reads them.)
|
|
"""
|
|
|
|
journey_id: str
|
|
label: str
|
|
capability_pattern: List[str] = Field(default_factory=list) # OUTPUT cluster: the delta this journey is about
|
|
assumed_capabilities: List[str] = Field(default_factory=list) # INPUT cluster: typically already present
|
|
industry: Optional[str] = None
|
|
product_type: Optional[str] = None
|
|
target_type: Optional[str] = None # context only: regulation / certification / contract / environmental
|
|
|
|
|
|
class MatchContext(BaseModel):
|
|
"""Optional corroborating context — surfaced as documented reasons, never part of the score."""
|
|
|
|
industry: Optional[str] = None
|
|
product_type: Optional[str] = None
|
|
target_type: Optional[str] = None
|
|
|
|
|
|
class JourneyMatchReason(BaseModel):
|
|
"""The auditable WHY behind one match — everything a reviewer needs, no opaque score."""
|
|
|
|
matched_capabilities: List[str] = Field(default_factory=list) # delta INTERSECT pattern (what it explains)
|
|
unexplained_delta: List[str] = Field(default_factory=list) # delta - pattern (what it does NOT explain)
|
|
journey_only: List[str] = Field(default_factory=list) # pattern - delta (journey covers, not needed here)
|
|
context_signals: List[str] = Field(default_factory=list) # "gleiche Zielart", "gleiche Branche", ...
|
|
|
|
|
|
class JourneyMatch(BaseModel):
|
|
"""One known journey, ranked by how much of the delta it EXPLAINS (not how well it 'fits')."""
|
|
|
|
journey_id: str
|
|
label: str
|
|
score: float = 0.0 # |delta INTERSECT pattern| / |delta|, 0..1: share of the delta explained
|
|
explains: str = "" # "8 von 10 fehlenden Capabilities"
|
|
reason: JourneyMatchReason
|
|
|
|
|
|
class JourneyMatchResult(BaseModel):
|
|
"""Ranked known journeys that EXPLAIN a Capability Delta. Journey = explanation, not cause."""
|
|
|
|
delta_size: int = 0
|
|
matches: List[JourneyMatch] = Field(default_factory=list) # ranked desc by score
|
|
best: Optional[JourneyMatch] = None
|
|
headline: str = ""
|