diff --git a/internal/inventory/data.go b/internal/inventory/data.go index 54cc3af5..944ed3b8 100644 --- a/internal/inventory/data.go +++ b/internal/inventory/data.go @@ -195,7 +195,8 @@ func (i *Inventory) RecordData(ctx context.Context, machine string, declared []D where d.machine = $1 and d.module = $2 and d.item = $3 and not exists (select 1 from data_reading where machine = $1 and module = $2 and item = $3 and path = d.path - and at > $4::timestamptz - $7::interval)`, + and at > $4::timestamptz - $7::interval) + on conflict (machine, module, item, at) do nothing`, machine, d.Module, d.Item, *m.MeasuredAt, *m.Size, m.LastWrite, fmt.Sprintf("%d seconds", int(readingEvery.Seconds()))); err != nil { return change, err diff --git a/internal/inventory/data_test.go b/internal/inventory/data_test.go index 0c12fbe6..9d23e49e 100644 --- a/internal/inventory/data_test.go +++ b/internal/inventory/data_test.go @@ -172,3 +172,22 @@ func TestThePeakIsReadAtTheItemsPathOnly(t *testing.T) { t.Fatalf("back at the first path the peak is %v (%v), want 94700000", peaks, err) } } + +// Two measurements at the same moment at two paths keep one reading and fail nothing: the second +// would otherwise break the reading's key and lose the machine's whole record (issue 368 review). +func TestTwoPathsAtOneMomentFailNothing(t *testing.T) { + inv := fresh(t) + ctx := t.Context() + now := time.Date(2026, 10, 10, 0, 0, 0, 0, time.UTC) + declared := []DeclaredData{{Module: "claude-code", Item: "agent-home", Class: "valuable", Owned: true}} + for _, path := range []string{"/home/operator/.claude", "/home/agent/.claude"} { + if _, err := inv.RecordData(ctx, "novox", declared, map[string]map[string]Measurement{"claude-code": { + "agent-home": {Path: path, Size: size(100), MeasuredAt: at(now)}}}, "", now); err != nil { + t.Fatalf("a measurement at %s failed: %v", path, err) + } + } + r, err := inv.DataOf(ctx, "novox", "claude-code", "agent-home") + if err != nil || r.Path != "/home/agent/.claude" { + t.Fatalf("%+v, %v", r, err) + } +}