Skip to content

Commit a798312

Browse files
committed
fix(ci): stabilize memory migration ordering and matrix checks
Preserve deterministic append/input order for timestamp-tied memory entries to avoid flaky migration assertions, and replace rg with portable grep in Apple matrix validation so CI runners without ripgrep pass. Made-with: Cursor
1 parent bd572af commit a798312

3 files changed

Lines changed: 49 additions & 20 deletions

File tree

Scripts/validate-apple-matrix.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ fi
1111

1212
required_platform_tokens=(".iOS(" ".macOS(" ".tvOS(" ".watchOS(")
1313
for token in "${required_platform_tokens[@]}"; do
14-
if ! rg -q --fixed-strings "${token}" "Package.swift"; then
14+
if ! grep -Fq "${token}" "Package.swift"; then
1515
echo "Missing platform declaration token '${token}' in Package.swift"
1616
exit 1
1717
fi
@@ -31,8 +31,8 @@ for path in "${required_share_extension_files[@]}"; do
3131
done
3232

3333
# Ensure iOS 26-only APIs stay availability-gated.
34-
if rg -q "BGContinuedProcessingTask|BGContinuedProcessingTaskRequest" "Examples/iOS/OpenClawiOS/OpenClawiOS"; then
35-
if ! rg -q "@available\\(iOS 26\\.0, \\*\\)" "Examples/iOS/OpenClawiOS/OpenClawiOS"; then
34+
if grep -ERq "BGContinuedProcessingTask|BGContinuedProcessingTaskRequest" "Examples/iOS/OpenClawiOS/OpenClawiOS"; then
35+
if ! grep -ERq "@available\\(iOS 26\\.0, \\*\\)" "Examples/iOS/OpenClawiOS/OpenClawiOS"; then
3636
echo "Detected iOS 26 APIs without availability guard."
3737
exit 1
3838
fi

Sources/OpenClawMemory/ConversationMemoryStore.swift

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -181,14 +181,28 @@ public actor ConversationMemoryStore: ConversationMemoryStoreProtocol {
181181

182182
/// Returns all entries across sessions sorted by timestamp.
183183
public func allEntries() -> [ConversationMemoryEntry] {
184-
self.entriesBySession.values
185-
.flatMap { $0 }
186-
.sorted { lhs, rhs in
187-
if lhs.createdAtMs == rhs.createdAtMs {
188-
return lhs.id < rhs.id
184+
let flattened: [(entry: ConversationMemoryEntry, sessionKey: String, index: Int)] = self.entriesBySession
185+
.flatMap { sessionKey, entries in
186+
entries.enumerated().map { offset, entry in
187+
(entry, sessionKey, offset)
189188
}
190-
return lhs.createdAtMs < rhs.createdAtMs
191189
}
190+
let ordered = flattened.sorted { lhs, rhs in
191+
if lhs.entry.createdAtMs != rhs.entry.createdAtMs {
192+
return lhs.entry.createdAtMs < rhs.entry.createdAtMs
193+
}
194+
if lhs.sessionKey == rhs.sessionKey {
195+
return lhs.index < rhs.index
196+
}
197+
if lhs.sessionKey != rhs.sessionKey {
198+
return lhs.sessionKey < rhs.sessionKey
199+
}
200+
if lhs.index != rhs.index {
201+
return lhs.index < rhs.index
202+
}
203+
return lhs.entry.id < rhs.entry.id
204+
}
205+
return ordered.map(\.entry)
192206
}
193207

194208
private func append(_ entry: ConversationMemoryEntry) {

Sources/OpenClawMemory/SwiftDataMemoryGraphStore.swift

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -162,14 +162,28 @@ public actor SwiftDataMemoryGraphStore: ConversationMemoryStoreProtocol {
162162

163163
/// Returns all memory graph entries in chronological order.
164164
public func allEntries() -> [ConversationMemoryEntry] {
165-
self.entriesBySession.values
166-
.flatMap { $0 }
167-
.sorted { lhs, rhs in
168-
if lhs.createdAtMs == rhs.createdAtMs {
169-
return lhs.id < rhs.id
165+
let flattened: [(entry: ConversationMemoryEntry, sessionKey: String, index: Int)] = self.entriesBySession
166+
.flatMap { sessionKey, entries in
167+
entries.enumerated().map { offset, entry in
168+
(entry, sessionKey, offset)
170169
}
171-
return lhs.createdAtMs < rhs.createdAtMs
172170
}
171+
let ordered = flattened.sorted { lhs, rhs in
172+
if lhs.entry.createdAtMs != rhs.entry.createdAtMs {
173+
return lhs.entry.createdAtMs < rhs.entry.createdAtMs
174+
}
175+
if lhs.sessionKey == rhs.sessionKey {
176+
return lhs.index < rhs.index
177+
}
178+
if lhs.sessionKey != rhs.sessionKey {
179+
return lhs.sessionKey < rhs.sessionKey
180+
}
181+
if lhs.index != rhs.index {
182+
return lhs.index < rhs.index
183+
}
184+
return lhs.entry.id < rhs.entry.id
185+
}
186+
return ordered.map(\.entry)
173187
}
174188

175189
/// Imports pre-existing memory entries, preserving IDs/timestamps.
@@ -193,12 +207,13 @@ public actor SwiftDataMemoryGraphStore: ConversationMemoryStoreProtocol {
193207

194208
private func appendEntries(_ entries: [ConversationMemoryEntry]) {
195209
guard !entries.isEmpty else { return }
196-
let sorted = entries.sorted { lhs, rhs in
197-
if lhs.createdAtMs == rhs.createdAtMs {
198-
return lhs.id < rhs.id
210+
let sorted = entries.enumerated().sorted { lhs, rhs in
211+
if lhs.element.createdAtMs != rhs.element.createdAtMs {
212+
return lhs.element.createdAtMs < rhs.element.createdAtMs
199213
}
200-
return lhs.createdAtMs < rhs.createdAtMs
201-
}
214+
// Keep original caller order when timestamps collide.
215+
return lhs.offset < rhs.offset
216+
}.map(\.element)
202217
var existingIDs = Set(self.allEntries().map(\.id))
203218
for entry in sorted {
204219
guard !existingIDs.contains(entry.id) else {

0 commit comments

Comments
 (0)