diff --git a/.gitignore b/.gitignore
index 37ae0ee..d2317c1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -79,6 +79,7 @@ play-store/
create_feature_graphic.py
# Tool caches and local state
+.devenv/
.cachebro/
.osgrep/
third_party/lpcnet_flutter/
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index 18e8f43..efeaa9a 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -489,7 +489,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
- CURRENT_PROJECT_VERSION = 129;
+ CURRENT_PROJECT_VERSION = 130;
DEVELOPMENT_TEAM = JND55328G8;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
@@ -511,7 +511,7 @@
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 129;
+ CURRENT_PROJECT_VERSION = 130;
DEVELOPMENT_TEAM = JND55328G8;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
@@ -530,7 +530,7 @@
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 129;
+ CURRENT_PROJECT_VERSION = 130;
DEVELOPMENT_TEAM = JND55328G8;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
@@ -547,7 +547,7 @@
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 129;
+ CURRENT_PROJECT_VERSION = 130;
DEVELOPMENT_TEAM = JND55328G8;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
@@ -679,7 +679,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
- CURRENT_PROJECT_VERSION = 129;
+ CURRENT_PROJECT_VERSION = 130;
DEVELOPMENT_TEAM = JND55328G8;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
@@ -702,7 +702,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
- CURRENT_PROJECT_VERSION = 129;
+ CURRENT_PROJECT_VERSION = 130;
DEVELOPMENT_TEAM = JND55328G8;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index f3cba8c..8acee1d 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -43,7 +43,7 @@
CFBundleSignature
????
CFBundleVersion
- 129
+ 130
LSRequiresIPhoneOS
ITSAppUsesNonExemptEncryption
diff --git a/ios/fastlane/report.xml b/ios/fastlane/report.xml
index 914341a..b93922a 100644
--- a/ios/fastlane/report.xml
+++ b/ios/fastlane/report.xml
@@ -5,22 +5,22 @@
-
+
-
+
-
+
-
+
diff --git a/lib/services/path_history_service.dart b/lib/services/path_history_service.dart
index 5fa10aa..a492793 100644
--- a/lib/services/path_history_service.dart
+++ b/lib/services/path_history_service.dart
@@ -214,7 +214,9 @@ class PathHistoryService {
pathBytes: selection.pathBytes.toList(),
hopCount: selection.hopCount,
hashSize: selection.hashSize,
- source: existing?.source ?? PathRecordSource.learned,
+ source: success
+ ? PathRecordSource.learned
+ : existing?.source ?? PathRecordSource.learned,
successCount: (existing?.successCount ?? 0) + (success ? 1 : 0),
failureCount: (existing?.failureCount ?? 0) + (success ? 0 : 1),
lastRoundTripTimeMs: success
diff --git a/pubspec.yaml b/pubspec.yaml
index bc3db95..8c77059 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
-version: 2026.0324.1+43
+version: 2026.0402.1+44
environment:
sdk: ^3.9.2
diff --git a/test/services/path_history_service_test.dart b/test/services/path_history_service_test.dart
index 8f16107..05d7df1 100644
--- a/test/services/path_history_service_test.dart
+++ b/test/services/path_history_service_test.dart
@@ -207,6 +207,42 @@ void main() {
},
);
+ test(
+ 'confirmed direct delivery promotes an observed path to learned',
+ () async {
+ final service = PathHistoryService();
+ final contact = _buildContact(
+ seed: 4,
+ pathBytes: [0xAA, 0xBB],
+ hopCount: 2,
+ hashSize: 1,
+ );
+
+ await service.initialize();
+ await service.recordReceivedBytePath(contact.publicKeyHex, [
+ 0xBB,
+ 0xAA,
+ ], 1);
+ await service.recordPathResult(
+ contact.publicKeyHex,
+ PathSelection(
+ mode: PathSelectionMode.directHistorical,
+ pathBytes: Uint8List.fromList([0xAA, 0xBB]),
+ hopCount: 2,
+ hashSize: 1,
+ ),
+ success: true,
+ roundTripTimeMs: 150,
+ );
+
+ final history = service.historyFor(contact.publicKeyHex);
+ expect(history.directPaths, hasLength(1));
+ expect(history.directPaths.single.source, PathRecordSource.learned);
+ expect(history.directPaths.single.successCount, 1);
+ expect(history.directPaths.single.lastRoundTripTimeMs, 150);
+ },
+ );
+
test('clear history removes stored direct paths for one contact', () async {
final service = PathHistoryService();