mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 02:25:56 +00:00
Canonicalize export redaction labels
This commit is contained in:
@@ -145,6 +145,10 @@ That export decision must come from the shared unified-resource privacy
|
||||
helpers, so sensitivity floors and redaction-triggered routing stay aligned
|
||||
with the canonical policy contract instead of being recomputed in AI-local
|
||||
code.
|
||||
The export audit should also record canonical human-readable redaction labels
|
||||
from the shared policy presentation helper, so the audit trail and the
|
||||
resource-context surfaces speak the same governed redaction language instead
|
||||
of reformatting hint names locally.
|
||||
The same AI runtime boundary now also consumes the canonical unified-resource
|
||||
timeline when it assembles rich resource or incident context. Recent-change
|
||||
context should come from the shared resource store first so AI prompts reflect
|
||||
|
||||
@@ -448,6 +448,10 @@ Shared privacy helpers also own the export sensitivity floor and route
|
||||
decision derived from those canonical policy counts, so AI export audits and
|
||||
route decisions reuse the same governed boundary logic instead of rebuilding
|
||||
it in consumer packages.
|
||||
That same export path now records canonical human-readable redaction labels
|
||||
through the shared policy presentation helper, so audit records and prompt
|
||||
context stay aligned on the same redaction vocabulary instead of duplicating
|
||||
hint-to-label conversion in AI-local code.
|
||||
The AI runtime now also uses the canonical policy presentation helpers to
|
||||
surface those routing and redaction labels in shared context output, so the
|
||||
same policy model is reflected in prompt summaries instead of being
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -42,16 +41,7 @@ func (s *Service) recordUnifiedResourceExport(destinationModel, summary string,
|
||||
return
|
||||
}
|
||||
|
||||
redactions := make([]string, 0, len(redactionHints))
|
||||
for _, hint := range redactionHints {
|
||||
redaction := strings.TrimSpace(string(hint))
|
||||
if redaction == "" {
|
||||
continue
|
||||
}
|
||||
redactions = append(redactions, redaction)
|
||||
}
|
||||
sort.Strings(redactions)
|
||||
redactions = uniqueStrings(redactions)
|
||||
redactions := unifiedresources.ResourceRedactionLabelsFromHints(redactionHints)
|
||||
|
||||
sensitivityFloor := unifiedresources.ExportSensitivityFloor(sensitivityCounts)
|
||||
decision, reason := unifiedresources.ExportDecisionForContext(sensitivityFloor, localOnlyCount, len(redactions))
|
||||
@@ -122,19 +112,3 @@ func (s *Service) recordUnifiedResourceExport(destinationModel, summary string,
|
||||
Msg("failed to persist unified resource export audit")
|
||||
}
|
||||
}
|
||||
|
||||
func uniqueStrings(values []string) []string {
|
||||
if len(values) <= 1 {
|
||||
return values
|
||||
}
|
||||
out := make([]string, 0, len(values))
|
||||
last := ""
|
||||
for _, value := range values {
|
||||
if value == "" || value == last {
|
||||
continue
|
||||
}
|
||||
out = append(out, value)
|
||||
last = value
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ func TestRecordUnifiedResourceExport_UsesCanonicalPrivacyHelpers(t *testing.T) {
|
||||
},
|
||||
wantDecision: unifiedresources.ExportRedacted,
|
||||
wantReason: "governed unified resource context exported in redacted form",
|
||||
wantRedactions: []string{"hostname", "path"},
|
||||
wantRedactions: []string{"Hostname", "Path"},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -346,6 +346,9 @@ func TestResourcePolicyLabelHelpersUsedByAIConsumers(t *testing.T) {
|
||||
"unifiedresources.ResourcePolicyLabel(",
|
||||
"unifiedresources.ResourcePolicyRedactedValue(",
|
||||
},
|
||||
filepath.Join("..", "ai", "resource_export.go"): {
|
||||
"unifiedresources.ResourceRedactionLabelsFromHints(redactionHints)",
|
||||
},
|
||||
filepath.Join("..", "ai", "resource_context.go"): {
|
||||
"unifiedresources.ResourcePolicyLabel(",
|
||||
"unifiedresources.ResourcePolicyRedactedValue(",
|
||||
@@ -359,6 +362,7 @@ func TestResourcePolicyLabelHelpersUsedByAIConsumers(t *testing.T) {
|
||||
"func ResourcePolicyLabel(name, aiSafeSummary string, policy *ResourcePolicy) string",
|
||||
"func ResourcePolicyRedactedValue(value string, policy *ResourcePolicy, hints ...ResourceRedactionHint) string",
|
||||
"const ResourcePolicyRedactedLabel = \"redacted by policy\"",
|
||||
"func ResourceRedactionLabelsFromHints(hints []ResourceRedactionHint) []string",
|
||||
"func ResourceDisplayName(resource Resource) string",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -236,6 +236,24 @@ func TestResourcePolicyLabelHelpers(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceRedactionLabelsFromHints(t *testing.T) {
|
||||
got := ResourceRedactionLabelsFromHints([]ResourceRedactionHint{
|
||||
ResourceRedactionPath,
|
||||
ResourceRedactionHostname,
|
||||
ResourceRedactionHostname,
|
||||
ResourceRedactionAlias,
|
||||
})
|
||||
want := []string{"Hostname", "Alias", "Path"}
|
||||
if len(got) != len(want) {
|
||||
t.Fatalf("labels = %#v, want %#v", got, want)
|
||||
}
|
||||
for i := range want {
|
||||
if got[i] != want[i] {
|
||||
t.Fatalf("labels[%d] = %q, want %q", i, got[i], want[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceDisplayName(t *testing.T) {
|
||||
if got := ResourceDisplayName(Resource{
|
||||
Name: " node-a ",
|
||||
|
||||
@@ -97,6 +97,18 @@ func ResourcePolicyRedactionLabels(policy *ResourcePolicy) []string {
|
||||
return ResourcePolicyRedactionLabelsFromCounts(counts)
|
||||
}
|
||||
|
||||
// ResourceRedactionLabelsFromHints returns canonical human-readable labels for a hint slice.
|
||||
func ResourceRedactionLabelsFromHints(hints []ResourceRedactionHint) []string {
|
||||
if len(hints) == 0 {
|
||||
return nil
|
||||
}
|
||||
counts := make(map[ResourceRedactionHint]int, len(hints))
|
||||
for _, hint := range hints {
|
||||
counts[hint]++
|
||||
}
|
||||
return ResourcePolicyRedactionLabelsFromCounts(counts)
|
||||
}
|
||||
|
||||
// ResourcePolicyRedactionLabelsFromCounts returns the canonical labels for the
|
||||
// redaction hints present in the provided count map.
|
||||
func ResourcePolicyRedactionLabelsFromCounts(counts map[ResourceRedactionHint]int) []string {
|
||||
|
||||
Reference in New Issue
Block a user