package ucca import ( "fmt" "os" "path/filepath" "runtime" ) // graphCallerRel resolves a path relative to THIS source file (build-time location), so the // graph data is findable under `go test` (cwd = package dir) regardless of working directory. // In a built container the source is gone, so cwd-relative candidates carry the load instead. func graphCallerRel(rel string) string { _, file, _, ok := runtime.Caller(0) if !ok { return "" } return filepath.Join(filepath.Dir(file), rel) } // firstExisting returns the first candidate path that exists with the requested kind (dir vs // file). Empty candidates (e.g. unset env overrides) are skipped. func firstExisting(candidates []string, wantDir bool) string { for _, p := range candidates { if p == "" { continue } info, err := os.Stat(p) if err != nil || info.IsDir() != wantDir { continue } return p } return "" } // LoadComplianceGraph loads the file-backed Compliance Execution Graph: the Registry join-key // contract (obligations/obligation_join_keys.json — owned by the Obligation session) + our // curated, accepted control mappings + evidence requirements. Locations are resolved across // three layouts: dev (cwd = ai-compliance-sdk/, canonical contract at ../obligations), container // (WORKDIR /app, data/ copied in incl. a synced data/obligations/ copy) and `go test` // (cwd = package dir, via graphCallerRel). Fail-closed: a missing/invalid source returns an // error so the handler serves 503 — never a half-built graph. // // NOTE: data/obligations/obligation_join_keys.json is a SYNCED COPY of the repo-root contract // (the canonical owner is the Obligation session). Re-sync it when the Registry grows; dev/test // prefer the canonical repo-root path, only the container falls back to the copy. func LoadComplianceGraph() (*ObligationJoinKeys, *ControlMappingSet, *EvidenceRequirementSet, error) { joinPath := firstExisting([]string{ os.Getenv("BP_OBLIGATION_JOIN_KEYS"), "../obligations/obligation_join_keys.json", graphCallerRel("../../../obligations/obligation_join_keys.json"), "data/obligations/obligation_join_keys.json", graphCallerRel("../../data/obligations/obligation_join_keys.json"), }, false) if joinPath == "" { return nil, nil, nil, fmt.Errorf("obligation_join_keys.json not found in any candidate path") } mapDir := firstExisting([]string{ os.Getenv("BP_CONTROL_MAPPINGS_DIR"), "data/control_mappings", graphCallerRel("../../data/control_mappings"), }, true) if mapDir == "" { return nil, nil, nil, fmt.Errorf("control_mappings dir not found in any candidate path") } evDir := firstExisting([]string{ os.Getenv("BP_EVIDENCE_DIR"), "data/evidence_requirements", graphCallerRel("../../data/evidence_requirements"), }, true) if evDir == "" { return nil, nil, nil, fmt.Errorf("evidence_requirements dir not found in any candidate path") } joins, err := LoadObligationJoinKeys(joinPath) if err != nil { return nil, nil, nil, fmt.Errorf("load join keys (%s): %w", joinPath, err) } mappings, err := LoadControlMappings(mapDir) if err != nil { return nil, nil, nil, fmt.Errorf("load control mappings (%s): %w", mapDir, err) } evidence, err := LoadEvidenceRequirements(evDir) if err != nil { return nil, nil, nil, fmt.Errorf("load evidence (%s): %w", evDir, err) } return joins, mappings, evidence, nil }