@githolon/dsl 0.119.9 → 0.119.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,8 @@ import 'dart:async';
2
2
 
3
3
  import 'package:nomos_types/nomos_kernel_protocol.dart' as nomos_protocol;
4
4
 
5
+ import 'nomos_trace.dart';
6
+
5
7
  import 'application_model_adoption.dart' show NomosModelAdoptionProgress;
6
8
 
7
9
  /// Live byte progress reported by the peer runtime for one custody transfer.
@@ -587,6 +589,10 @@ final class NmsSyncStatusProjection {
587
589
  final Map<String, SyncStatus> _bySession = {};
588
590
  final Map<String, StreamController<SyncStatus>> _sessionStreams = {};
589
591
  SyncStatus _current = const SyncStatus();
592
+ // Keep the session result separately from readiness overlays. A final follow
593
+ // or preparation report can then release checking without another session
594
+ // event, and cannot erase a session's error, offline or pending state.
595
+ SyncStatus _applicationInput = const SyncStatus();
590
596
  int? _followed;
591
597
  int? _followsInSync;
592
598
  int? _followsLive;
@@ -738,7 +744,7 @@ final class NmsSyncStatusProjection {
738
744
  if (other != workspace)
739
745
  pushSession(other, released(other, _bySession[other]!));
740
746
  }
741
- _pushApplication(released('', _current));
747
+ _pushApplication(released('', _applicationInput));
742
748
  }
743
749
 
744
750
  void _republishRepairs(String workspace) {
@@ -759,7 +765,7 @@ final class NmsSyncStatusProjection {
759
765
  for (final other in _bySession.keys.toList(growable: false)) {
760
766
  if (other != workspace) pushSession(other, _bySession[other]!);
761
767
  }
762
- _pushApplication(_current);
768
+ _pushApplication(_applicationInput);
763
769
  }
764
770
 
765
771
  Stream<SyncStatus> get applicationStream => _application.stream;
@@ -794,7 +800,7 @@ final class NmsSyncStatusProjection {
794
800
  _followStatuses = List.unmodifiable(statuses);
795
801
  _followReportAt = observedAt;
796
802
  _pushApplication(
797
- _current.copyWith(
803
+ _applicationInput.copyWith(
798
804
  followed: followed,
799
805
  followsInSync: inSync,
800
806
  followsLive: live,
@@ -810,7 +816,7 @@ final class NmsSyncStatusProjection {
810
816
  _preparingWorkspaces = preparing;
811
817
  _retryingWorkspaces = retrying;
812
818
  _preparationActivity = activity;
813
- _pushApplication(_current.copyWith(
819
+ _pushApplication(_applicationInput.copyWith(
814
820
  preparingWorkspaces: preparing,
815
821
  retryingWorkspaces: retrying,
816
822
  preparationActivity: activity,
@@ -824,7 +830,7 @@ final class NmsSyncStatusProjection {
824
830
  required SyncStatus Function(SyncStatus current) change,
825
831
  }) {
826
832
  if (session == null || session.isEmpty) {
827
- _pushApplication(change(_current));
833
+ _pushApplication(change(_applicationInput));
828
834
  return;
829
835
  }
830
836
  pushSession(session, change(statusFor(session)));
@@ -850,6 +856,12 @@ final class NmsSyncStatusProjection {
850
856
  final previous = _bySession[session];
851
857
  if (previous != null && _sameStatus(previous, status)) return;
852
858
  _bySession[session] = status;
859
+ if (NomosTrace.enabled) {
860
+ NomosTrace.emit('dart', 'sync.session',
861
+ 'session=$session phase=${status.phase.name} live=${status.live} '
862
+ 'ahead=${status.ahead} behind=${status.behind} '
863
+ 'owed=${_unshared.containsKey(session)} copy=${status.localCopy.name}');
864
+ }
853
865
  if (!_changes.isClosed) _changes.add(null);
854
866
  final controller = _sessionStreams[session];
855
867
  if (controller != null && !controller.isClosed) controller.add(status);
@@ -970,6 +982,7 @@ final class NmsSyncStatusProjection {
970
982
  }
971
983
 
972
984
  void _pushApplication(SyncStatus input) {
985
+ _applicationInput = input;
973
986
  var status = input.copyWith(
974
987
  repairingCopies: repairingCopies, unsharedWork: unsharedWork);
975
988
  if (_unshared.isNotEmpty && status.phase == SyncPhase.idle) {
@@ -978,8 +991,32 @@ final class NmsSyncStatusProjection {
978
991
  } else {
979
992
  _heldBack.remove('');
980
993
  }
994
+ // Bound interactions are only part of the application's inventory. A
995
+ // background follow may still be checking/preparing or have queued writes.
996
+ // Its complete live report can withhold currency, never manufacture it for
997
+ // an unknown or pending session. Keep the underlying session counts intact.
998
+ final followsPending = _followed != null && _followed! > 0 &&
999
+ (_followsInSync != _followed || _followsLive != _followed ||
1000
+ _followStatuses.length != _followed ||
1001
+ _followStatuses.any((follow) => !follow.current ||
1002
+ (follow.unsent ?? 0) > 0 || (follow.unacknowledged ?? 0) > 0));
1003
+ if (status.phase == SyncPhase.idle &&
1004
+ (followsPending || _preparingWorkspaces > 0 || _retryingWorkspaces > 0 ||
1005
+ _repairing.isNotEmpty)) {
1006
+ status = status.copyWith(phase: SyncPhase.checking, live: false);
1007
+ }
981
1008
  if (_sameStatus(_current, status)) return;
982
1009
  _current = status;
1010
+ if (NomosTrace.enabled) {
1011
+ final pending = _bySession.entries
1012
+ .where((entry) => entry.value.phase != SyncPhase.idle)
1013
+ .map((entry) => '${entry.key}:${entry.value.phase.name}')
1014
+ .join(',');
1015
+ NomosTrace.emit('dart', 'sync.aggregate',
1016
+ 'phase=${status.phase.name} live=${status.live} '
1017
+ 'followed=${status.followed} settled=${status.followsInSync} '
1018
+ 'owed=${_unshared.length} pending=[$pending]');
1019
+ }
983
1020
  if (!_application.isClosed) _application.add(status);
984
1021
  }
985
1022
 
package/dart/pubspec.lock CHANGED
@@ -222,7 +222,7 @@ packages:
222
222
  path: "../dart_types"
223
223
  relative: true
224
224
  source: path
225
- version: "0.15.2"
225
+ version: "0.15.3"
226
226
  package_config:
227
227
  dependency: "direct main"
228
228
  description:
package/dart/pubspec.yaml CHANGED
@@ -7,7 +7,7 @@ repository: https://github.com/Captain-App/nomos2
7
7
  description: >-
8
8
  Nomos 2 Dart client — generated domain values, intent payloads and application
9
9
  lifecycle types over the typed Nomos kernel protocol and USDA custody model.
10
- version: 0.70.10
10
+ version: 0.70.12
11
11
 
12
12
  environment:
13
13
  sdk: ^3.5.0
@@ -18,7 +18,7 @@ dependencies:
18
18
  # The pure-data core (wire types, offer codec, NomosReads, schema validation, error
19
19
  # info). nomos_client depends on and re-exports it, so existing imports are unchanged.
20
20
  # For local monorepo dev, pubspec_overrides.yaml points this at ../dart_types.
21
- nomos_types: ^0.15.2
21
+ nomos_types: ^0.15.3
22
22
  fixnum: ^1.1.1
23
23
  json_schema: ^5.2.2
24
24
  crypto: ^3.0.0
@@ -0,0 +1,246 @@
1
+ import 'dart:async';
2
+ import 'package:fixnum/fixnum.dart';
3
+ import 'package:nomos_client/src/peer_application_streams.dart';
4
+ import 'package:nomos_client/src/background_work.dart';
5
+ import 'package:nomos_client/src/nms_generated_runtime.dart';
6
+ import 'package:nomos_types/nomos_kernel_protocol.dart' as p;
7
+ import 'package:test/test.dart';
8
+
9
+ final class _Office
10
+ with NomosRuntimeV2KernelCalls
11
+ implements PeerOfficeConnection {
12
+ final events = StreamController<p.RuntimeEvent>.broadcast(sync: true);
13
+ int sequence = 0;
14
+ @override
15
+ Stream<p.RuntimeEvent> get runtimeEvents => events.stream;
16
+ @override
17
+ int nextRuntimeRequestId() => ++sequence;
18
+ @override
19
+ Future<p.RuntimeResponse> invokeRuntime(p.RuntimeRequest request) async {
20
+ if (request.hasBindSession()) {
21
+ return p.RuntimeResponse(
22
+ protocolVersion: 2,
23
+ requestId: request.requestId,
24
+ sessionBound: p.SessionBound(
25
+ workspace: request.bindSession.workspace,
26
+ session: request.bindSession.session,
27
+ credentialCapability: request.bindSession.session));
28
+ }
29
+ if (request.hasCloseRealm()) {
30
+ return p.RuntimeResponse(
31
+ protocolVersion: 2,
32
+ requestId: request.requestId,
33
+ realmClosed: p.RealmClosed());
34
+ }
35
+ fail('Unexpected command: ${request.whichCommand()}');
36
+ }
37
+
38
+ void link(String workspace, bool open) => events.add(p.RuntimeEvent(
39
+ session: workspace,
40
+ linkChanged: p.LinkChanged(
41
+ status: open
42
+ ? p.LinkStatus.LINK_STATUS_OPEN
43
+ : p.LinkStatus.LINK_STATUS_CLOSED)));
44
+ void round(String workspace,
45
+ {bool deferred = false,
46
+ bool progressed = false,
47
+ bool awaitingSeal = false}) =>
48
+ events.add(p.RuntimeEvent(
49
+ session: workspace,
50
+ syncCompleted: p.SyncCompleted(
51
+ result: p.SyncWorkspaceResponse(
52
+ workspace: workspace,
53
+ previousHead: 'head',
54
+ currentHead: 'head',
55
+ deferred: deferred ? Int64.ONE : Int64.ZERO,
56
+ directOfferDeferred: deferred,
57
+ stillAhead: deferred || awaitingSeal,
58
+ pulled: progressed,
59
+ mainAdvanced: progressed,
60
+ offerReceipt: awaitingSeal
61
+ ? p.CustodyOfferReceipt(
62
+ admission: p.CustodyOfferAdmissionReceipt(
63
+ main: 'head', sealedMain: 'older'))
64
+ : null))));
65
+ }
66
+
67
+ void main() {
68
+ late _Office office;
69
+ late DartReception reception;
70
+ setUp(() async {
71
+ office = _Office();
72
+ reception = DartReception(office);
73
+ for (final workspace in ['estate', 'catalogue']) {
74
+ await reception.bindSession(workspace: workspace, session: workspace);
75
+ office.link(workspace, true);
76
+ office.round(workspace);
77
+ }
78
+ });
79
+ tearDown(() async {
80
+ await reception.dispose();
81
+ await office.events.close();
82
+ });
83
+ test(
84
+ 'automatic projection refreshes preserve updates without claiming user priority',
85
+ () async {
86
+ final streams = NmsPeerApplicationStreamView(
87
+ session: reception.session('estate'),
88
+ statusProjection: (_) => throw UnimplementedError(),
89
+ );
90
+ final backgroundReads = <bool>[];
91
+ final subscription = streams.refreshOn(
92
+ read: () async {
93
+ await Future<void>.delayed(Duration.zero);
94
+ backgroundReads.add(nomosInBackground);
95
+ return backgroundReads.length;
96
+ },
97
+ changes: [streams.workspaceChanges],
98
+ ).listen((_) {});
99
+ addTearDown(subscription.cancel);
100
+ await Future<void>.delayed(const Duration(milliseconds: 10));
101
+ expect(backgroundReads, [false],
102
+ reason: 'the initial visible read retains user priority');
103
+ office.round('catalogue');
104
+ await Future<void>.delayed(const Duration(milliseconds: 10));
105
+ expect(backgroundReads, [false, true],
106
+ reason: 'cross-workspace projections still refresh automatically');
107
+ office.round('estate', progressed: true);
108
+ await Future<void>.delayed(const Duration(milliseconds: 10));
109
+ expect(backgroundReads, [false, true, true]);
110
+ expect(nomosInBackground, isFalse,
111
+ reason: 'automatic work must not demote subsequent user actions');
112
+ });
113
+ test('a refresh coalesced behind the initial read runs in background',
114
+ () async {
115
+ final streams = NmsPeerApplicationStreamView(
116
+ session: reception.session('estate'),
117
+ statusProjection: (_) => throw UnimplementedError(),
118
+ );
119
+ final initial = Completer<void>();
120
+ final priorities = <bool>[];
121
+ final done = Completer<void>();
122
+ final subscription = streams.refreshOn(
123
+ read: () async {
124
+ priorities.add(nomosInBackground);
125
+ if (priorities.length == 1) await initial.future;
126
+ if (priorities.length == 2) done.complete();
127
+ return priorities.length;
128
+ },
129
+ changes: [streams.workspaceChanges],
130
+ ).listen((_) {});
131
+ addTearDown(subscription.cancel);
132
+ await Future<void>.delayed(Duration.zero);
133
+ office.round('catalogue');
134
+ await Future<void>.delayed(Duration.zero);
135
+ initial.complete();
136
+ await done.future.timeout(const Duration(seconds: 1));
137
+ expect(priorities, [false, true]);
138
+ });
139
+ test(
140
+ 'CAP-791: final reconnect result releases both session and aggregate debt',
141
+ () {
142
+ for (final workspace in ['estate', 'catalogue'])
143
+ office.link(workspace, false);
144
+ expect(reception.currentStatus.phase, SyncPhase.offline);
145
+ for (final workspace in ['estate', 'catalogue'])
146
+ office.link(workspace, true);
147
+ expect(reception.currentStatus.phase, SyncPhase.checking);
148
+ office.round('estate', deferred: true);
149
+ office.round('catalogue');
150
+ expect(reception.currentStatus.phase, SyncPhase.syncing);
151
+ office.round('estate', progressed: true);
152
+ expect(reception.session('estate').currentStatus.phase, SyncPhase.idle);
153
+ expect(reception.currentStatus.unsharedWork, isEmpty);
154
+ expect(reception.currentStatus.phase, SyncPhase.idle,
155
+ reason: 'no later event may be needed to refold released debt');
156
+ expect(reception.currentStatus.live, isTrue);
157
+ expect(reception.currentStatus.ahead, 0);
158
+ });
159
+ test('one recovered workspace cannot clear another workspace\'s debt', () {
160
+ office.round('estate', deferred: true);
161
+ office.round('catalogue', deferred: true);
162
+ office.round('estate', progressed: true);
163
+ expect(reception.currentStatus.phase, SyncPhase.syncing);
164
+ expect(reception.currentStatus.unsharedWork.keys, ['catalogue']);
165
+ office.round('catalogue', progressed: true);
166
+ expect(reception.currentStatus.phase, SyncPhase.idle);
167
+ });
168
+ test('no-progress result cannot clear a real deferral', () {
169
+ office.round('estate', deferred: true);
170
+ office.round('estate');
171
+ expect(reception.currentStatus.phase, SyncPhase.syncing);
172
+ expect(reception.currentStatus.unsharedWork.keys, ['estate']);
173
+ });
174
+ test('clearing debt cannot conceal an offline sibling or unsealed admission',
175
+ () {
176
+ office.round('estate', deferred: true);
177
+ office.link('catalogue', false);
178
+ office.round('estate', progressed: true);
179
+ expect(reception.currentStatus.phase, SyncPhase.offline);
180
+ office.link('catalogue', true);
181
+ office.round('catalogue', awaitingSeal: true);
182
+ expect(reception.currentStatus.phase, SyncPhase.awaitingRemoteDurability);
183
+ office.round('catalogue');
184
+ expect(reception.currentStatus.phase, SyncPhase.idle);
185
+ });
186
+ test('settled inventory does not override an unknown late-bound session',
187
+ () async {
188
+ await reception.bindSession(workspace: 'foreign', session: 'foreign');
189
+ office.events.add(p.RuntimeEvent(
190
+ followsSummary:
191
+ p.FollowsSummary(followed: 3, settled: 3, live: 3, workspaces: [
192
+ for (final name in ['estate', 'catalogue', 'foreign'])
193
+ p.FollowObservation(
194
+ workspace: name,
195
+ phase: p.FollowObservationPhase.FOLLOW_OBSERVATION_PHASE_SETTLED,
196
+ live: true)
197
+ ])));
198
+ expect(reception.currentStatus.phase, SyncPhase.checking);
199
+ expect(reception.currentStatus.ahead, isNull);
200
+ // The peer's exact-head retained-follow result, not the tally, confirms it.
201
+ office.round('foreign');
202
+ office.link('foreign', true);
203
+ expect(reception.currentStatus.phase, SyncPhase.idle);
204
+ expect(reception.currentStatus.live, isTrue);
205
+ });
206
+
207
+ test('a genuine failure remains visible when another workspace recovers', () {
208
+ office.round('estate', deferred: true);
209
+ office.events.add(p.RuntimeEvent(
210
+ session: 'catalogue',
211
+ syncCompleted: p.SyncCompleted(
212
+ failure: p.RuntimeFailure(
213
+ code: 'auth.denied', message: 'Access revoked'))));
214
+ office.round('estate', progressed: true);
215
+ expect(reception.currentStatus.phase, SyncPhase.error);
216
+ expect(reception.currentStatus.lastError, contains('Access revoked'));
217
+ });
218
+ test(
219
+ 'application waits for unbound background follows and releases on their report',
220
+ () {
221
+ void follows(int settled) => office.events.add(p.RuntimeEvent(
222
+ followsSummary: p.FollowsSummary(
223
+ followed: 3,
224
+ settled: settled,
225
+ live: 3,
226
+ workspaces: [
227
+ for (final name in ['estate', 'catalogue', 'foreign'])
228
+ p.FollowObservation(
229
+ workspace: name,
230
+ live: true,
231
+ phase: name == 'foreign' && settled != 3
232
+ ? p.FollowObservationPhase
233
+ .FOLLOW_OBSERVATION_PHASE_RUNNING
234
+ : p.FollowObservationPhase
235
+ .FOLLOW_OBSERVATION_PHASE_SETTLED)
236
+ ])));
237
+ follows(2);
238
+ expect(reception.currentStatus.phase, SyncPhase.checking);
239
+ expect(reception.currentStatus.live, isFalse);
240
+ office.round('estate');
241
+ expect(reception.currentStatus.phase, SyncPhase.checking);
242
+ follows(3);
243
+ expect(reception.currentStatus.phase, SyncPhase.idle);
244
+ expect(reception.currentStatus.live, isTrue);
245
+ });
246
+ }
@@ -26,4 +26,74 @@ void main() {
26
26
  expect(projection.applicationStatus.preparationActivity, isNull);
27
27
  await projection.close();
28
28
  });
29
+ test('preparation keeps a settled application non-current until it finishes',
30
+ () async {
31
+ final projection = NmsSyncStatusProjection();
32
+ addTearDown(projection.close);
33
+ projection.pushSession(
34
+ 'estate',
35
+ const SyncStatus(
36
+ phase: SyncPhase.idle, ahead: 0, behind: 0, live: true));
37
+ projection.foldApplication(['estate']);
38
+ projection.updatePreparationCounts(1, 1, activity: 'Preparing catalogue');
39
+ expect(projection.applicationStatus.phase, SyncPhase.checking);
40
+ expect(projection.applicationStatus.live, isFalse);
41
+ projection.foldApplication(['estate']);
42
+ expect(projection.applicationStatus.phase, SyncPhase.checking);
43
+ projection.updatePreparationCounts(0, 0);
44
+ expect(projection.applicationStatus.phase, SyncPhase.idle);
45
+ expect(projection.applicationStatus.live, isTrue);
46
+ });
47
+
48
+ test(
49
+ 'completed follow/preparation reports never clear session-owned debt or failures',
50
+ () async {
51
+ for (final phase in [
52
+ SyncPhase.checking,
53
+ SyncPhase.syncing,
54
+ SyncPhase.awaitingRemoteDurability,
55
+ SyncPhase.offline,
56
+ SyncPhase.error
57
+ ]) {
58
+ final projection = NmsSyncStatusProjection();
59
+ addTearDown(projection.close);
60
+ projection.pushSession('estate', SyncStatus(phase: phase));
61
+ projection.foldApplication(['estate']);
62
+ projection.updatePreparationCounts(1, 1);
63
+ projection.updateFollows(1, 1, live: 1, statuses: const [
64
+ WorkspaceFollowStatus(
65
+ workspace: 'estate',
66
+ phase: WorkspaceFollowPhase.settled,
67
+ live: true)
68
+ ]);
69
+ projection.updatePreparationCounts(0, 0);
70
+ expect(projection.applicationStatus.phase, phase);
71
+ expect(projection.applicationStatus.ahead, isNull);
72
+ }
73
+ });
74
+
75
+ test('queued stream writes and incomplete reports withhold currency',
76
+ () async {
77
+ final projection = NmsSyncStatusProjection();
78
+ addTearDown(projection.close);
79
+ projection.pushSession(
80
+ 'estate',
81
+ const SyncStatus(
82
+ phase: SyncPhase.idle, ahead: 0, behind: 0, live: true));
83
+ projection.foldApplication(['estate']);
84
+ projection.updateFollows(1, 1, live: 1);
85
+ expect(projection.applicationStatus.phase, SyncPhase.checking);
86
+ for (final (unsent, unacknowledged) in [(1, 0), (0, 1), (0, 0)]) {
87
+ projection.updateFollows(1, 1, live: 1, statuses: [
88
+ WorkspaceFollowStatus(
89
+ workspace: 'estate',
90
+ phase: WorkspaceFollowPhase.settled,
91
+ live: true,
92
+ unsent: unsent,
93
+ unacknowledged: unacknowledged)
94
+ ]);
95
+ expect(projection.applicationStatus.phase,
96
+ unsent + unacknowledged == 0 ? SyncPhase.idle : SyncPhase.checking);
97
+ }
98
+ });
29
99
  }
@@ -37,12 +37,14 @@ void main() {
37
37
  ),
38
38
  ),
39
39
  );
40
- expect(reads.active, isTrue,
41
- reason: 'a read index still being built is real preparation');
40
+ expect(
41
+ reads.active,
42
+ isTrue,
43
+ reason: 'a read index still being built is real preparation',
44
+ );
42
45
  });
43
46
 
44
- test('upkeep backing off after a failure is not a sync problem (CAP-693)',
45
- () {
47
+ test('upkeep backing off after a failure is not a sync problem (CAP-693)', () {
46
48
  // A shallow device's Git upkeep was refused by the kernel; the status turned orange though
47
49
  // every read and write still worked.
48
50
  final failingUpkeep = WorkspacePreparationReport.fromMaintenance(
@@ -63,7 +65,39 @@ void main() {
63
65
  retryAfterMillis: 4000,
64
66
  ),
65
67
  );
66
- expect(failingReads.retrying, isTrue,
67
- reason: 'preparation a read waits on still shows');
68
+ expect(
69
+ failingReads.retrying,
70
+ isTrue,
71
+ reason: 'preparation a read waits on still shows',
72
+ );
68
73
  });
74
+
75
+ test(
76
+ 'a pack tidy that fails after a sync is upkeep backing off, not a sync problem (CAP-952)',
77
+ () {
78
+ // Prod web, 13 Oct 2026: the kernel refused a repack after an incoming sync ("repack header
79
+ // …: object could not be found"). The sync icon stayed amber on "Still preparing" for the
80
+ // back-off while all 34 workspaces were up to date.
81
+ final failingAfterSync = WorkspacePreparationReport.fromMaintenance(
82
+ protocol.ReadMaintenanceProgress(
83
+ workspace: 'estate',
84
+ cause: protocol.ReadMaintenanceCause.READ_MAINTENANCE_CAUSE_SYNC,
85
+ retryAfterMillis: 1800000,
86
+ ),
87
+ );
88
+ expect(failingAfterSync.retrying, isFalse);
89
+ expect(failingAfterSync.upkeepRetrying, isTrue);
90
+ expect(failingAfterSync.active, isFalse);
91
+
92
+ // The read index a sync owes travels under its own read cause and still counts.
93
+ final indexAfterSync = WorkspacePreparationReport.fromMaintenance(
94
+ protocol.ReadMaintenanceProgress(
95
+ workspace: 'estate',
96
+ cause: protocol.ReadMaintenanceCause.READ_MAINTENANCE_CAUSE_RESIDENCY,
97
+ retryAfterMillis: 1000,
98
+ ),
99
+ );
100
+ expect(indexAfterSync.retrying, isTrue);
101
+ },
102
+ );
69
103
  }
@@ -1,3 +1,11 @@
1
+ ## 0.15.3
2
+
3
+ - Add a typed physical network-restored observation to wake transport retries without changing sync evidence.
4
+
5
+ - `HostHealth.ingress` (`KernelIngressSnapshot`) in the runtime v2 residency protocol: the kernel execution ingress a host reports (CAP-964).
6
+
7
+ - `KernelIngressSnapshot` reports posted and waiting requests per lane, the in-flight window, abandoned requests and deduplicated law-product imports (CAP-967).
8
+
1
9
  ## 0.15.2
2
10
 
3
11
  Add the shared kernel protocol fields for owned pack import continuations, cancellation and deferred admission.
@@ -12270,6 +12270,104 @@ class PeerGitRefsFetched extends $pb.GeneratedMessage {
12270
12270
  void clearDeferred() => $_clearField(3);
12271
12271
  }
12272
12272
 
12273
+ /// Physical host observation: the network returned after being unavailable.
12274
+ /// Wake eligible retries once; this is never a sync or durability receipt.
12275
+ class NetworkRestoredRequest extends $pb.GeneratedMessage {
12276
+ factory NetworkRestoredRequest() => create();
12277
+
12278
+ NetworkRestoredRequest._();
12279
+
12280
+ factory NetworkRestoredRequest.fromBuffer($core.List<$core.int> data,
12281
+ [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
12282
+ create()..mergeFromBuffer(data, registry);
12283
+ factory NetworkRestoredRequest.fromJson($core.String json,
12284
+ [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
12285
+ create()..mergeFromJson(json, registry);
12286
+
12287
+ static final $pb.BuilderInfo _i = $pb.BuilderInfo(
12288
+ _omitMessageNames ? '' : 'NetworkRestoredRequest',
12289
+ package:
12290
+ const $pb.PackageName(_omitMessageNames ? '' : 'nomos.runtime.v2'),
12291
+ createEmptyInstance: create)
12292
+ ..hasRequiredFields = false;
12293
+
12294
+ @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
12295
+ NetworkRestoredRequest clone() => deepCopy();
12296
+ @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
12297
+ NetworkRestoredRequest copyWith(
12298
+ void Function(NetworkRestoredRequest) updates) =>
12299
+ super.copyWith((message) => updates(message as NetworkRestoredRequest))
12300
+ as NetworkRestoredRequest;
12301
+
12302
+ @$core.override
12303
+ $pb.BuilderInfo get info_ => _i;
12304
+
12305
+ @$core.pragma('dart2js:noInline')
12306
+ static NetworkRestoredRequest create() => NetworkRestoredRequest._();
12307
+ @$core.override
12308
+ NetworkRestoredRequest createEmptyInstance() => create();
12309
+ @$core.pragma('dart2js:noInline')
12310
+ static NetworkRestoredRequest getDefault() => _defaultInstance ??=
12311
+ $pb.GeneratedMessage.$_defaultFor<NetworkRestoredRequest>(create);
12312
+ static NetworkRestoredRequest? _defaultInstance;
12313
+ }
12314
+
12315
+ class NetworkRestored extends $pb.GeneratedMessage {
12316
+ factory NetworkRestored({
12317
+ $core.int? linksRetried,
12318
+ }) {
12319
+ final result = create();
12320
+ if (linksRetried != null) result.linksRetried = linksRetried;
12321
+ return result;
12322
+ }
12323
+
12324
+ NetworkRestored._();
12325
+
12326
+ factory NetworkRestored.fromBuffer($core.List<$core.int> data,
12327
+ [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
12328
+ create()..mergeFromBuffer(data, registry);
12329
+ factory NetworkRestored.fromJson($core.String json,
12330
+ [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
12331
+ create()..mergeFromJson(json, registry);
12332
+
12333
+ static final $pb.BuilderInfo _i = $pb.BuilderInfo(
12334
+ _omitMessageNames ? '' : 'NetworkRestored',
12335
+ package:
12336
+ const $pb.PackageName(_omitMessageNames ? '' : 'nomos.runtime.v2'),
12337
+ createEmptyInstance: create)
12338
+ ..aI(1, _omitFieldNames ? '' : 'linksRetried',
12339
+ fieldType: $pb.PbFieldType.OU3)
12340
+ ..hasRequiredFields = false;
12341
+
12342
+ @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
12343
+ NetworkRestored clone() => deepCopy();
12344
+ @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
12345
+ NetworkRestored copyWith(void Function(NetworkRestored) updates) =>
12346
+ super.copyWith((message) => updates(message as NetworkRestored))
12347
+ as NetworkRestored;
12348
+
12349
+ @$core.override
12350
+ $pb.BuilderInfo get info_ => _i;
12351
+
12352
+ @$core.pragma('dart2js:noInline')
12353
+ static NetworkRestored create() => NetworkRestored._();
12354
+ @$core.override
12355
+ NetworkRestored createEmptyInstance() => create();
12356
+ @$core.pragma('dart2js:noInline')
12357
+ static NetworkRestored getDefault() => _defaultInstance ??=
12358
+ $pb.GeneratedMessage.$_defaultFor<NetworkRestored>(create);
12359
+ static NetworkRestored? _defaultInstance;
12360
+
12361
+ @$pb.TagNumber(1)
12362
+ $core.int get linksRetried => $_getIZ(0);
12363
+ @$pb.TagNumber(1)
12364
+ set linksRetried($core.int value) => $_setUnsignedInt32(0, value);
12365
+ @$pb.TagNumber(1)
12366
+ $core.bool hasLinksRetried() => $_has(0);
12367
+ @$pb.TagNumber(1)
12368
+ void clearLinksRetried() => $_clearField(1);
12369
+ }
12370
+
12273
12371
  const $core.bool _omitFieldNames =
12274
12372
  $core.bool.fromEnvironment('protobuf.omit_field_names');
12275
12373
  const $core.bool _omitMessageNames =