From 06bb73662d32d65d1e775a4dd35f67d82d673e40 Mon Sep 17 00:00:00 2001
From: Matthias Nott <mnott@mnsoft.org>
Date: Mon, 06 Apr 2026 15:32:03 +0200
Subject: [PATCH] feat: rewrite message store - append-only log, sync routing, eliminates async race conditions
---
lib/providers/providers.dart | 73 ++++++++----------------------------
1 files changed, 16 insertions(+), 57 deletions(-)
diff --git a/lib/providers/providers.dart b/lib/providers/providers.dart
index 6155189..06fe159 100644
--- a/lib/providers/providers.dart
+++ b/lib/providers/providers.dart
@@ -98,73 +98,48 @@
String? get currentSessionId => _currentSessionId;
- /// Switch to a new session and load its messages.
- Future<void> switchSession(String sessionId) async {
- final trace = StackTrace.current.toString().split('\n').take(4).join(' | ');
- TraceService.instance.addTrace(
- 'switchSession',
- 'from=${_currentSessionId?.substring(0, 8) ?? "null"}(${state.length}) → ${sessionId.substring(0, 8)} | $trace',
- );
- // Write current session to disk
- if (_currentSessionId != null && state.isNotEmpty) {
- await MessageStore.writeDirect(_currentSessionId!, state);
- }
-
- // Skip reload if staying on the same session — messages are already in memory
+ /// Switch to a session. SYNCHRONOUS — no async gap, no race with incoming
+ /// messages. MessageStoreV2.loadSession reads from the in-memory index.
+ void switchSession(String sessionId) {
if (_currentSessionId == sessionId) {
- TraceService.instance.addTrace('switchSession SKIP', 'already on ${sessionId.substring(0, 8)}');
+ TraceService.instance.addTrace(
+ 'switchSession SKIP', 'already on ${sessionId.substring(0, 8)}');
return;
}
-
+ TraceService.instance.addTrace(
+ 'switchSession',
+ 'from=${_currentSessionId?.substring(0, 8) ?? "null"}(${state.length}) → ${sessionId.substring(0, 8)}',
+ );
_currentSessionId = sessionId;
- final messages = await MessageStore.loadAll(sessionId);
- // Merge: if addMessage ran during loadAll and added messages for THIS session,
- // they'll be in state but not in the loaded messages. Keep the longer list.
- if (state.length > messages.length && _currentSessionId == sessionId) {
- TraceService.instance.addTrace('switchSession MERGE', 'kept ${state.length} (loaded ${messages.length})');
- } else {
- state = messages;
- }
+ state = MessageStoreV2.loadSession(sessionId);
}
- /// Add a message to the current session.
+ /// Add a message to the current session (display + append-only persist).
void addMessage(Message message) {
state = [...state, message];
if (_currentSessionId != null) {
- MessageStore.save(_currentSessionId!, state);
+ MessageStoreV2.append(_currentSessionId!, message);
}
}
- /// Update a message by ID.
+ /// Update a message by ID (in-memory only — patch is not persisted to log).
void updateMessage(String id, Message Function(Message) updater) {
state = state.map((m) => m.id == id ? updater(m) : m).toList();
- if (_currentSessionId != null) {
- MessageStore.save(_currentSessionId!, state);
- }
}
- /// Remove a message by ID.
+ /// Remove a message by ID (in-memory only).
void removeMessage(String id) {
state = state.where((m) => m.id != id).toList();
- if (_currentSessionId != null) {
- MessageStore.save(_currentSessionId!, state);
- }
}
- /// Remove all messages matching a predicate.
+ /// Remove all messages matching a predicate (in-memory only).
void removeWhere(bool Function(Message) test) {
state = state.where((m) => !test(m)).toList();
- if (_currentSessionId != null) {
- MessageStore.save(_currentSessionId!, state);
- }
}
- /// Clear all messages for the current session.
+ /// Clear all messages for the current session (in-memory only).
void clearMessages() {
state = [];
- if (_currentSessionId != null) {
- MessageStore.save(_currentSessionId!, state);
- }
}
void updateContent(String messageId, String content) {
@@ -185,22 +160,6 @@
else
m,
];
- if (_currentSessionId != null) {
- MessageStore.save(_currentSessionId!, state);
- }
- }
-
- /// Load more (older) messages for pagination.
- Future<void> loadMore() async {
- if (_currentSessionId == null) return;
- final older = await MessageStore.load(
- _currentSessionId!,
- offset: state.length,
- limit: 50,
- );
- if (older.isNotEmpty) {
- state = [...older, ...state];
- }
}
}
--
Gitblit v1.3.1