@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.
package/dart/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 0.70.12
2
+
3
+ - Reconcile a completed round's outstanding work and copy repairs before folding application sync status, so a successful reconnect clears the aggregate pending state immediately. Add opt-in session and aggregate sync trace provenance.
4
+ - Keep application sync pending while background follows, stream queues or preparation remain outstanding; release that state on their final report without requiring another session event.
5
+ - Keep pending-commit counts available from retained local history when the network link is closed; do not turn missing custody into a zero count.
6
+
7
+ Run event-triggered application projection refreshes in the shared background scheduling context. Preserve cross-workspace updates and initial visible-read priority without repeatedly delaying native sync.
8
+
9
+ ## 0.70.11
10
+
11
+ - A pack tidy that fails after a sync no longer shows the application as still preparing: the read index a sync owes is reported under its own read cause, and a SYNC report is custody upkeep alone (CAP-952).
12
+
1
13
  ## 0.70.10
2
14
 
3
15
  Deliver the shared runtime's owned, bounded pack import continuations and cancellation with the matching kernel protocol, preventing concurrent workspace acquisition from dropping an active import.
@@ -0,0 +1,11 @@
1
+ import 'dart:async';
2
+
3
+ final Object _background = Object();
4
+
5
+ /// Marks automatic application work for the host scheduler. This does not
6
+ /// change authorization, results or sync evidence; user requests keep priority.
7
+ Future<T> nomosRunInBackground<T>(Future<T> Function() body) =>
8
+ runZoned(body, zoneValues: {_background: true});
9
+
10
+ /// Inherited through awaited continuations and stream callbacks in this zone.
11
+ bool get nomosInBackground => Zone.current[_background] == true;
@@ -1,3 +1,4 @@
1
+ import 'background_work.dart' show nomosRunInBackground;
1
2
  import 'application_status.dart' show ApplicationStatus;
2
3
  import 'nms_generated_runtime.dart' show NomosSession, SyncStatus;
3
4
  import 'workspace_open_progress.dart' show ApplicationWorkspaceOpenProgress;
@@ -75,7 +76,7 @@ final class NmsPeerApplicationStreamView implements NmsPeerApplicationStreams {
75
76
  var reading = false;
76
77
  var reread = false;
77
78
 
78
- Future<void> refresh() async {
79
+ Future<void> refresh({bool automatic = false}) async {
79
80
  if (reading) {
80
81
  reread = true;
81
82
  return;
@@ -84,18 +85,22 @@ final class NmsPeerApplicationStreamView implements NmsPeerApplicationStreams {
84
85
  do {
85
86
  reread = false;
86
87
  try {
87
- final value = await read();
88
+ final value =
89
+ await (automatic ? nomosRunInBackground(read) : read());
88
90
  if (!events.isClosed) events.add(value);
89
91
  } catch (error, stackTrace) {
90
92
  if (!events.isClosed) events.addError(error, stackTrace);
91
93
  }
94
+ // A change coalesced during the initial visible read is automatic
95
+ // too; it must not inherit that first read's user priority.
96
+ automatic = true;
92
97
  } while (reread && !events.isClosed);
93
98
  reading = false;
94
99
  }
95
100
 
96
101
  final subscriptions = changes
97
102
  .map((stream) => stream.listen(
98
- (_) => refresh(),
103
+ (_) => refresh(automatic: true),
99
104
  onError: events.addError,
100
105
  ))
101
106
  .toList(growable: false);
@@ -971,10 +971,6 @@ class DartReception {
971
971
  }
972
972
  }
973
973
  final observedAt = DateTime.now();
974
- _eventPlane._updateScopedStatus(
975
- session,
976
- (current) => reduceSyncCompletion(current, completed, observedAt),
977
- );
978
974
  // A round may "complete" over a damaged copy (nothing pulled, head unchanged) while its
979
975
  // kernel cannot read a commit of it; that is reported as a separate failure. Either says
980
976
  // the copy is being repaired, and a refetch that replaced it says it is repaired.
@@ -997,6 +993,23 @@ class DartReception {
997
993
  at: observedAt, detail: nmsOwedWorkDetail(result));
998
994
  }
999
995
  }
996
+ // Reconcile the round's owed-work/repair facts BEFORE reducing and folding
997
+ // its result. Otherwise a released session becomes idle after the fold,
998
+ // leaving the application syncing until some unrelated event (CAP-791).
999
+ _eventPlane._updateScopedStatus(
1000
+ session,
1001
+ (current) => reduceSyncCompletion(current, completed, observedAt),
1002
+ );
1003
+ if (NomosTrace.enabled) {
1004
+ final status = session == null
1005
+ ? syncStatuses.applicationStatus
1006
+ : syncStatuses.statusFor(session);
1007
+ NomosTrace.emit('dart', 'sync.completed',
1008
+ 'session=$session reason=${completed.reason} '
1009
+ 'phase=${status.phase.name} live=${status.live} '
1010
+ 'owed=${syncStatuses.unsharedWork.length} '
1011
+ 'aggregate=${syncStatuses.applicationStatus.phase.name}');
1012
+ }
1000
1013
  return;
1001
1014
  }
1002
1015
  if (event.hasLawChanged()) {
@@ -55,47 +55,56 @@ final class WorkspacePreparationReport {
55
55
  WorkspacePreparationReport? previous,
56
56
  }) {
57
57
  final kernel = progress.hasKernel() ? progress.kernel : null;
58
- final retry =
59
- progress.hasRetryAfterMillis() ? progress.retryAfterMillis : null;
58
+ final retry = progress.hasRetryAfterMillis()
59
+ ? progress.retryAfterMillis
60
+ : null;
60
61
  final step = progress.satisfiedByRead
61
62
  ? WorkspacePreparationStep.complete
62
63
  : kernel == null
63
- ? switch (progress.cause) {
64
- protocol.ReadMaintenanceCause
65
- .READ_MAINTENANCE_CAUSE_DOMAIN_PREPARATION =>
66
- WorkspacePreparationStep.preparingWrites,
67
- protocol.ReadMaintenanceCause.READ_MAINTENANCE_CAUSE_OPEN ||
68
- protocol
69
- .ReadMaintenanceCause.READ_MAINTENANCE_CAUSE_READ_DEBT ||
70
- protocol
71
- .ReadMaintenanceCause.READ_MAINTENANCE_CAUSE_RESIDENCY =>
72
- WorkspacePreparationStep.preparingReads,
73
- _ => WorkspacePreparationStep.preparingWorkspace,
74
- }
75
- : switch (kernel.phase) {
76
- protocol.MaintenancePhase.MAINTENANCE_PHASE_READ_DOCUMENTS ||
77
- protocol.MaintenancePhase.MAINTENANCE_PHASE_SQLITE_INDEX ||
78
- protocol.MaintenancePhase.MAINTENANCE_PHASE_READ_INDEX_CACHE =>
79
- WorkspacePreparationStep.preparingReads,
80
- protocol.MaintenancePhase.MAINTENANCE_PHASE_COMMAND_FRONTIER ||
81
- protocol.MaintenancePhase.MAINTENANCE_PHASE_COMMAND_ROUTE =>
82
- WorkspacePreparationStep.preparingWrites,
83
- protocol.MaintenancePhase.MAINTENANCE_PHASE_GIT_CUSTODY =>
84
- WorkspacePreparationStep.preparingCustody,
85
- protocol.MaintenancePhase.MAINTENANCE_PHASE_COMPLETE =>
86
- WorkspacePreparationStep.complete,
87
- _ => WorkspacePreparationStep.preparingWorkspace,
88
- };
89
- // Packing Git custody after writes, or upkeep merely scheduled because this peer's own
90
- // writes were admitted, never holds a read or a write: the workspace is ready meanwhile.
91
- final upkeep = step == WorkspacePreparationStep.preparingCustody ||
64
+ ? switch (progress.cause) {
65
+ protocol
66
+ .ReadMaintenanceCause
67
+ .READ_MAINTENANCE_CAUSE_DOMAIN_PREPARATION =>
68
+ WorkspacePreparationStep.preparingWrites,
69
+ protocol.ReadMaintenanceCause.READ_MAINTENANCE_CAUSE_OPEN ||
70
+ protocol.ReadMaintenanceCause.READ_MAINTENANCE_CAUSE_READ_DEBT ||
71
+ protocol.ReadMaintenanceCause.READ_MAINTENANCE_CAUSE_RESIDENCY =>
72
+ WorkspacePreparationStep.preparingReads,
73
+ _ => WorkspacePreparationStep.preparingWorkspace,
74
+ }
75
+ : switch (kernel.phase) {
76
+ protocol.MaintenancePhase.MAINTENANCE_PHASE_READ_DOCUMENTS ||
77
+ protocol.MaintenancePhase.MAINTENANCE_PHASE_SQLITE_INDEX ||
78
+ protocol.MaintenancePhase.MAINTENANCE_PHASE_READ_INDEX_CACHE =>
79
+ WorkspacePreparationStep.preparingReads,
80
+ protocol.MaintenancePhase.MAINTENANCE_PHASE_COMMAND_FRONTIER ||
81
+ protocol.MaintenancePhase.MAINTENANCE_PHASE_COMMAND_ROUTE =>
82
+ WorkspacePreparationStep.preparingWrites,
83
+ protocol.MaintenancePhase.MAINTENANCE_PHASE_GIT_CUSTODY =>
84
+ WorkspacePreparationStep.preparingCustody,
85
+ protocol.MaintenancePhase.MAINTENANCE_PHASE_COMPLETE =>
86
+ WorkspacePreparationStep.complete,
87
+ _ => WorkspacePreparationStep.preparingWorkspace,
88
+ };
89
+ // Packing Git custody after writes or after a sync, or upkeep merely scheduled because
90
+ // commits were admitted or received, never holds a read or a write: the workspace is ready
91
+ // meanwhile. The read index a sync owes is reported under its own read cause, so a report
92
+ // carrying the SYNC cause is custody upkeep alone. Without SYNC here, one failed pack tidy
93
+ // after a sync showed the whole application as "still preparing" for its back-off (up to
94
+ // 30 minutes) while every workspace was up to date (prod web, 13 Oct 2026).
95
+ final upkeep =
96
+ step == WorkspacePreparationStep.preparingCustody ||
92
97
  (kernel == null &&
93
- progress.cause ==
94
- protocol.ReadMaintenanceCause
95
- .READ_MAINTENANCE_CAUSE_ADMITTED_OFFER);
98
+ (progress.cause ==
99
+ protocol
100
+ .ReadMaintenanceCause
101
+ .READ_MAINTENANCE_CAUSE_ADMITTED_OFFER ||
102
+ progress.cause ==
103
+ protocol.ReadMaintenanceCause.READ_MAINTENANCE_CAUSE_SYNC));
96
104
  return WorkspacePreparationReport(
97
105
  step: step,
98
- active: retry == null &&
106
+ active:
107
+ retry == null &&
99
108
  !upkeep &&
100
109
  !progress.stopped &&
101
110
  !progress.satisfiedByRead &&
@@ -108,18 +117,18 @@ final class WorkspacePreparationReport {
108
117
  gitFrontier: kernel == null
109
118
  ? null
110
119
  : kernel.hasGitFrontier()
111
- ? kernel.gitFrontier
112
- : previous?.gitFrontier,
120
+ ? kernel.gitFrontier
121
+ : previous?.gitFrontier,
113
122
  readFrontier: kernel == null
114
123
  ? null
115
124
  : kernel.hasReadFrontier()
116
- ? kernel.readFrontier
117
- : previous?.readFrontier,
125
+ ? kernel.readFrontier
126
+ : previous?.readFrontier,
118
127
  sqliteFrontier: kernel == null
119
128
  ? null
120
129
  : kernel.hasSqliteFrontier()
121
- ? kernel.sqliteFrontier
122
- : previous?.sqliteFrontier,
130
+ ? kernel.sqliteFrontier
131
+ : previous?.sqliteFrontier,
123
132
  );
124
133
  }
125
134