refactor(ucca): control-mapping model per review feedback

- DROP confidence from the persisted mapping: a curated mapping is a
  professional statement, not an AI guess (retriever score -> rationale only).
- ADD mapping_status (candidate|accepted|rejected|superseded) — the review state.
- ADD audit trail (reviewed_by/review_date/review_reason); accepted/rejected
  fail-closed without it.
- EXTEND mapping_type: + implements, + contradicts.
- Advisor truth = mapping_status=accepted (acceptedOnly filter).
- migrate the 18 CRA->OWASP rows to mapping_status=candidate.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-06-25 09:50:37 +02:00
parent 2f3c98fbe0
commit 53ea388ea0
3 changed files with 85 additions and 64 deletions
@@ -7,19 +7,25 @@ import (
)
func TestControlMapping_Validate(t *testing.T) {
valid := ControlMapping{SourceNorm: "CRA Annex I", TargetFramework: "OWASP ASVS", TargetControl: "V6.2.4", MappingType: "supports", Confidence: "high", Provenance: "human_curated"}
if err := valid.Validate(); err != nil {
t.Fatalf("valid mapping rejected: %v", err)
candidate := ControlMapping{SourceNorm: "CRA Annex I", TargetFramework: "OWASP ASVS", TargetControl: "V6.3.1", MappingType: "supports", MappingStatus: "candidate", Provenance: "retriever_candidate"}
if err := candidate.Validate(); err != nil {
t.Fatalf("valid candidate rejected: %v", err)
}
accepted := ControlMapping{SourceNorm: "A", TargetFramework: "X", TargetControl: "Y", MappingType: "implements", MappingStatus: "accepted", Provenance: "human_curated", ReviewedBy: "benjamin", ReviewDate: "2026-06-25", ReviewReason: "passt"}
if err := accepted.Validate(); err != nil {
t.Fatalf("valid accepted rejected: %v", err)
}
bad := []struct {
name string
m ControlMapping
}{
{"no source_norm", ControlMapping{TargetFramework: "X", TargetControl: "Y", MappingType: "supports", Confidence: "high", Provenance: "human_curated"}},
{"no target_control", ControlMapping{SourceNorm: "A", TargetFramework: "X", MappingType: "supports", Confidence: "high", Provenance: "human_curated"}},
{"bad mapping_type", ControlMapping{SourceNorm: "A", TargetFramework: "X", TargetControl: "Y", MappingType: "nope", Confidence: "high", Provenance: "human_curated"}},
{"bad confidence", ControlMapping{SourceNorm: "A", TargetFramework: "X", TargetControl: "Y", MappingType: "supports", Confidence: "huge", Provenance: "human_curated"}},
{"bad provenance", ControlMapping{SourceNorm: "A", TargetFramework: "X", TargetControl: "Y", MappingType: "supports", Confidence: "high", Provenance: "guessed"}},
{"no source_norm", ControlMapping{TargetFramework: "X", TargetControl: "Y", MappingType: "supports", MappingStatus: "candidate", Provenance: "retriever_candidate"}},
{"bad mapping_type", ControlMapping{SourceNorm: "A", TargetFramework: "X", TargetControl: "Y", MappingType: "nope", MappingStatus: "candidate", Provenance: "retriever_candidate"}},
{"bad mapping_status", ControlMapping{SourceNorm: "A", TargetFramework: "X", TargetControl: "Y", MappingType: "supports", MappingStatus: "maybe", Provenance: "retriever_candidate"}},
{"bad provenance", ControlMapping{SourceNorm: "A", TargetFramework: "X", TargetControl: "Y", MappingType: "supports", MappingStatus: "candidate", Provenance: "guessed"}},
{"accepted without audit trail", ControlMapping{SourceNorm: "A", TargetFramework: "X", TargetControl: "Y", MappingType: "supports", MappingStatus: "accepted", Provenance: "human_curated"}},
{"rejected without reason", ControlMapping{SourceNorm: "A", TargetFramework: "X", TargetControl: "Y", MappingType: "supports", MappingStatus: "rejected", Provenance: "human_curated", ReviewedBy: "b", ReviewDate: "2026-06-25"}},
}
for _, tt := range bad {
if err := tt.m.Validate(); err == nil {
@@ -31,8 +37,8 @@ func TestControlMapping_Validate(t *testing.T) {
func TestLoadControlMappings(t *testing.T) {
dir := t.TempDir()
content := `// header comment, ignored
{"source_norm":"CRA Annex I","source_role":"operational_requirement","target_framework":"OWASP ASVS","target_control":"V11.1.1","mapping_type":"supports","confidence":"high","provenance":"human_curated","rationale":"r","version":"2026-06-25"}
{"source_norm":"CRA Annex I","source_role":"operational_requirement","target_framework":"OWASP ASVS","target_control":"V6.2.4","mapping_type":"related","confidence":"low","provenance":"retriever_candidate","rationale":"r","version":"2026-06-25"}
{"source_norm":"CRA Annex I","source_role":"operational_requirement","target_framework":"OWASP ASVS","target_control":"V6.3.1","mapping_type":"supports","mapping_status":"accepted","provenance":"human_curated","reviewed_by":"benjamin","review_date":"2026-06-25","review_reason":"V6=Auth passt","rationale":"r","version":"2026-06-25"}
{"source_norm":"CRA Annex I","source_role":"operational_requirement","target_framework":"OWASP ASVS","target_control":"V14.2.4","mapping_type":"related","mapping_status":"candidate","provenance":"retriever_candidate","rationale":"r","version":"2026-06-25"}
`
if err := os.WriteFile(filepath.Join(dir, "m.jsonl"), []byte(content), 0o644); err != nil {
@@ -49,20 +55,21 @@ func TestLoadControlMappings(t *testing.T) {
t.Errorf("ControlsFor(all): want 2, got %d", len(got))
}
if got := set.ControlsFor("CRA Annex I", true); len(got) != 1 {
t.Errorf("ControlsFor(curatedOnly): want 1 (only human_curated), got %d", len(got))
t.Errorf("ControlsFor(acceptedOnly): want 1 (only accepted), got %d", len(got))
}
if got := set.ObligationsFor("OWASP ASVS", "V11.1.1", false); len(got) != 1 {
t.Errorf("ObligationsFor reverse lookup: want 1, got %d", len(got))
if got := set.ObligationsFor("OWASP ASVS", "V6.3.1", true); len(got) != 1 {
t.Errorf("ObligationsFor accepted reverse lookup: want 1, got %d", len(got))
}
}
func TestLoadControlMappings_RejectsInvalid(t *testing.T) {
dir := t.TempDir()
if err := os.WriteFile(filepath.Join(dir, "bad.jsonl"), []byte(`{"source_norm":"A","target_framework":"X","target_control":"Y","mapping_type":"BOGUS","confidence":"high","provenance":"human_curated"}`), 0o644); err != nil {
// accepted without the who/when/why audit trail must fail-closed.
if err := os.WriteFile(filepath.Join(dir, "bad.jsonl"), []byte(`{"source_norm":"A","target_framework":"X","target_control":"Y","mapping_type":"supports","mapping_status":"accepted","provenance":"human_curated","rationale":"r","version":"v"}`), 0o644); err != nil {
t.Fatal(err)
}
if _, err := LoadControlMappings(dir); err == nil {
t.Error("invalid mapping_type must fail the load (fail-closed audit store)")
t.Error("accepted mapping without audit trail must fail the load (fail-closed)")
}
}