diff --git a/internal/actionplanner/planner.go b/internal/actionplanner/planner.go index bf6f1beaa..cef752b34 100644 --- a/internal/actionplanner/planner.go +++ b/internal/actionplanner/planner.go @@ -108,7 +108,6 @@ func ResourceVersion(resource unified.Resource) string { Status unified.ResourceStatus `json:"status"` AISafeSummary string `json:"aiSafeSummary,omitempty"` Sources []unified.DataSource `json:"sources,omitempty"` - Identity normalizedIdentity `json:"identity,omitempty"` ParentID string `json:"parentId,omitempty"` Capabilities []normalizedCapabilityForResourceHash `json:"capabilities,omitempty"` Relationships []normalizedRelationship `json:"relationships,omitempty"` @@ -124,7 +123,6 @@ func ResourceVersion(resource unified.Resource) string { Status: resource.Status, AISafeSummary: strings.TrimSpace(resource.AISafeSummary), Sources: normalizeDataSources(resource.Sources), - Identity: normalizeIdentity(resource.Identity), Capabilities: normalizeCapabilitiesForResourceHash(resource.Capabilities), Relationships: normalizeRelationships(unified.ResourceRelationshipsWithCanonicalParent(resource)), RecentChanges: normalizeChanges(resource.RecentChanges), @@ -605,9 +603,8 @@ type storageIncidentHashFields struct { } type sourceStatusHash struct { - Status string `json:"status"` - LastSeen time.Time `json:"lastSeen,omitempty"` - Error string `json:"error,omitempty"` + Status string `json:"status"` + Error string `json:"error,omitempty"` } func normalizeDataSources(sources []unified.DataSource) []unified.DataSource { @@ -750,9 +747,8 @@ func normalizeSourceStatus(status map[unified.DataSource]unified.SourceStatus) m continue } out[normalizedSource] = sourceStatusHash{ - Status: strings.TrimSpace(sourceStatus.Status), - LastSeen: sourceStatus.LastSeen.UTC(), - Error: strings.TrimSpace(sourceStatus.Error), + Status: strings.TrimSpace(sourceStatus.Status), + Error: strings.TrimSpace(sourceStatus.Error), } } return out diff --git a/internal/actionplanner/planner_test.go b/internal/actionplanner/planner_test.go index d340d9dd7..9aba66d4b 100644 --- a/internal/actionplanner/planner_test.go +++ b/internal/actionplanner/planner_test.go @@ -175,10 +175,30 @@ func TestResourceVersionIgnoresObservationOnlyTimestamps(t *testing.T) { Status: unified.StatusOnline, LastSeen: time.Date(2026, 5, 3, 10, 0, 0, 0, time.UTC), UpdatedAt: time.Date(2026, 5, 3, 10, 0, 0, 0, time.UTC), + Identity: unified.ResourceIdentity{ + Hostnames: []string{"web-42"}, + IPAddresses: []string{"192.0.2.10"}, + }, + SourceStatus: map[unified.DataSource]unified.SourceStatus{ + unified.SourceProxmox: { + Status: "online", + LastSeen: time.Date(2026, 5, 3, 10, 0, 0, 0, time.UTC), + }, + }, } refreshed := base refreshed.LastSeen = base.LastSeen.Add(time.Minute) refreshed.UpdatedAt = base.UpdatedAt.Add(time.Minute) + refreshed.Identity = unified.ResourceIdentity{ + Hostnames: []string{"web-42", "web-42.local"}, + IPAddresses: []string{"192.0.2.10", "192.0.2.11"}, + } + refreshed.SourceStatus = map[unified.DataSource]unified.SourceStatus{ + unified.SourceProxmox: { + Status: "online", + LastSeen: base.SourceStatus[unified.SourceProxmox].LastSeen.Add(time.Minute), + }, + } if got, want := ResourceVersion(refreshed), ResourceVersion(base); got != want { t.Fatalf("ResourceVersion changed for observation-only timestamp drift: got %q want %q", got, want) diff --git a/internal/api/contract_test.go b/internal/api/contract_test.go index e096df553..460f75344 100644 --- a/internal/api/contract_test.go +++ b/internal/api/contract_test.go @@ -12712,7 +12712,7 @@ func TestContract_ActionPlanJSONSnapshot(t *testing.T) { } const want = `{ - "actionId":"act_371d0bcde73818752f83bb759e1fa523", + "actionId":"act_8f428171be2762cec97fd4546a291d5f", "requestId":"agent-run-123", "allowed":true, "requiresApproval":true, @@ -12722,9 +12722,9 @@ func TestContract_ActionPlanJSONSnapshot(t *testing.T) { "message":"Plan created for restart on web-42. Execution requires admin approval and is not performed by this endpoint.", "plannedAt":"2026-05-03T10:00:00Z", "expiresAt":"2026-05-03T10:05:00Z", - "resourceVersion":"resource:sha256:3a87c71cf83bd017736bcb25", + "resourceVersion":"resource:sha256:54fb6f0264f42e0f2724e513", "policyVersion":"policy:sha256:0bce3cd2df181ace685598eb", - "planHash":"sha256:38e8a016794bae597cd6129e65506556048bcac88d6a5a1b59e1337a2acc5a05", + "planHash":"sha256:69631faa9da67496a8b5953de2bc2dceb7b2aab5b582be3d1536e5d67683791b", "preflight":{ "target":"vm:42", "currentState":"web-42 is warning", @@ -12739,7 +12739,7 @@ func TestContract_ActionPlanJSONSnapshot(t *testing.T) { ], "verificationSteps":[ "Refresh the resource and confirm the expected state after execution.", - "Review /api/audit/actions/act_371d0bcde73818752f83bb759e1fa523/events for lifecycle evidence." + "Review /api/audit/actions/act_8f428171be2762cec97fd4546a291d5f/events for lifecycle evidence." ], "generatedAt":"2026-05-03T10:00:00Z" } @@ -12844,13 +12844,13 @@ func TestContract_ActionPlanAuditLifecycleSnapshot(t *testing.T) { } const want = `{ "audit":{ - "id":"act_f464b1a69ce73d27a986b445104ad708", + "id":"act_7eed41cdc58507f340151d2497707eca", "state":"pending_approval", "resourceId":"vm:42", "requestId":"agent-run-123", "requestedBy":"agent:oncall-helper", "approvalPolicy":"admin", - "planHash":"sha256:e2998e7fe1b9b97c8180102bd474ece7411146db70bbb6299cdc47582de7bd1d", + "planHash":"sha256:f60417e39f967eb0803af0a3f2e2abd70f20d8a77a09c2e36976bdda34b6dbaf", "preflightSummary":"No provider-supported dry run is advertised for this capability." }, "events":[