package ucca // ObligationStatus is the Advisor's vertical slice over the compliance graph for ONE legal // obligation: which accepted controls satisfy it, what evidence they require, what's missing, // and the resulting status. The point is "the required evidence is (not) present", not "a // document exists". citation_spans is pending until the Legal-Knowledge-Graph session attaches // them to the obligation (the upper half of the bridge). type ObligationStatus struct { ObligationID string `json:"obligation_id"` LegalBasis []string `json:"legal_basis"` // the obligation's citation_units Status string `json:"status"` // erfuellt | offen | unklar Controls []ObligationControlStatus `json:"controls"` CitationSpans string `json:"citation_spans"` // "pending" until the registry fills them } // ObligationControlStatus is one control under an obligation with its evidence picture. type ObligationControlStatus struct { Framework string `json:"framework"` Control string `json:"control"` MappingType string `json:"mapping_type"` RequiredEvidence []EvidenceRequirement `json:"required_evidence"` MissingEvidence []EvidenceRequirement `json:"missing_evidence"` } // AssessObligationStatus traverses obligation_id -> (citation_unit) -> accepted Controls -> // required Evidence -> Status. hasEvidence reports whether a given (framework, control, // evidence_type) is already collected; pass nil in the MVP (no collection yet) -> everything // required is missing and the status is "offen". Unknown or unmapped obligation -> "unklar". func AssessObligationStatus(joins *ObligationJoinKeys, mappings *ControlMappingSet, evidence *EvidenceRequirementSet, obligationID string, hasEvidence func(framework, control, evidenceType string) bool) ObligationStatus { ob := joins.FindObligation(obligationID) if ob == nil { return ObligationStatus{ObligationID: obligationID, Status: "unklar", CitationSpans: "pending"} } st := ObligationStatus{ ObligationID: obligationID, LegalBasis: ob.CitationUnits, CitationSpans: "pending", Controls: []ObligationControlStatus{}, } ctrls := AcceptedControlsForObligation(*ob, mappings) if len(ctrls) == 0 { st.Status = "unklar" // no accepted control reaches it — we cannot assess return st } anyMissing := false for _, m := range ctrls { req := evidence.RequiredFor(m.TargetFramework, m.TargetControl) missing := make([]EvidenceRequirement, 0, len(req)) for _, e := range req { if hasEvidence == nil || !hasEvidence(e.Framework, e.Control, e.EvidenceType) { missing = append(missing, e) } } if len(missing) > 0 { anyMissing = true } st.Controls = append(st.Controls, ObligationControlStatus{ Framework: m.TargetFramework, Control: m.TargetControl, MappingType: m.MappingType, RequiredEvidence: req, MissingEvidence: missing, }) } if anyMissing { st.Status = "offen" } else { st.Status = "erfuellt" } return st }