Skip to content

Commit b1f96d4

Browse files
fix(sync): re-export edited observations by checking UpdatedAt in filterNewData (#447) (#450)
Observations edited via mem_update have UpdatedAt > CreatedAt but unchanged CreatedAt, so the previous cutoff check (CreatedAt only) silently dropped them from every subsequent sync. Include the observation when either CreatedAt or UpdatedAt is after the cutoff so edits are always re-exported.
1 parent 841507f commit b1f96d4

2 files changed

Lines changed: 50 additions & 1 deletion

File tree

internal/sync/sync.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1112,7 +1112,7 @@ func (sy *Syncer) filterNewData(data *store.ExportData, lastChunkTime string) *C
11121112
}
11131113

11141114
for _, o := range data.Observations {
1115-
if normalizeTime(o.CreatedAt) > cutoff {
1115+
if normalizeTime(o.CreatedAt) > cutoff || normalizeTime(o.UpdatedAt) > cutoff {
11161116
chunk.Observations = append(chunk.Observations, o)
11171117
}
11181118
}

internal/sync/sync_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2198,6 +2198,55 @@ func TestFilterFunctionsAndTimeNormalization(t *testing.T) {
21982198
}
21992199
}
22002200

2201+
// TestFilterNewDataIncludesEditedObservations verifies that an observation whose
2202+
// CreatedAt is before the sync cutoff but whose UpdatedAt is after the cutoff is
2203+
// included in the filtered export (issue #447).
2204+
func TestFilterNewDataIncludesEditedObservations(t *testing.T) {
2205+
data := &store.ExportData{
2206+
Version: "0.1.0",
2207+
ExportedAt: "2025-01-01 00:00:00",
2208+
Observations: []store.Observation{
2209+
// created before cutoff, never edited -> should be EXCLUDED
2210+
{ID: 1, SessionID: "s1", CreatedAt: "2025-01-01 09:00:00", UpdatedAt: "2025-01-01 09:00:00"},
2211+
// created before cutoff, edited AFTER cutoff -> should be INCLUDED
2212+
{ID: 2, SessionID: "s1", CreatedAt: "2025-01-01 09:00:00", UpdatedAt: "2025-01-01 11:00:00"},
2213+
// created after cutoff -> should be INCLUDED (existing behaviour)
2214+
{ID: 3, SessionID: "s1", CreatedAt: "2025-01-01 11:00:00", UpdatedAt: "2025-01-01 11:00:00"},
2215+
},
2216+
}
2217+
2218+
cutoff := "2025-01-01T10:30:00Z"
2219+
sy := New(nil, t.TempDir())
2220+
filtered := sy.filterNewData(data, cutoff)
2221+
2222+
ids := make([]int64, 0, len(filtered.Observations))
2223+
for _, o := range filtered.Observations {
2224+
ids = append(ids, o.ID)
2225+
}
2226+
2227+
// ID 1 must be absent; IDs 2 and 3 must be present.
2228+
for _, id := range ids {
2229+
if id == 1 {
2230+
t.Fatalf("filterNewData included observation ID 1 (stale, unedited) — should have been excluded; ids=%v", ids)
2231+
}
2232+
}
2233+
found2, found3 := false, false
2234+
for _, id := range ids {
2235+
if id == 2 {
2236+
found2 = true
2237+
}
2238+
if id == 3 {
2239+
found3 = true
2240+
}
2241+
}
2242+
if !found2 {
2243+
t.Fatalf("filterNewData excluded observation ID 2 (edited after cutoff) — should have been included; ids=%v", ids)
2244+
}
2245+
if !found3 {
2246+
t.Fatalf("filterNewData excluded observation ID 3 (created after cutoff) — should have been included; ids=%v", ids)
2247+
}
2248+
}
2249+
22012250
func TestFilterByProjectEntityLevel(t *testing.T) {
22022251
projA := "proj-a"
22032252

0 commit comments

Comments
 (0)