@githolon/dsl 0.117.1 → 0.119.11
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/assets/operation-contract.wasm +0 -0
- package/compile_proof.mjs +16 -0
- package/dart/CHANGELOG.md +45 -0
- package/dart/lib/src/application_status.dart +58 -2
- package/dart/lib/src/peer_application_streams.dart +7 -1
- package/dart/lib/src/reception/dart_reception.dart +92 -12
- package/dart/lib/src/reception/reception_session.dart +10 -2
- package/dart/lib/src/reception/workspace_preparation_report.dart +50 -41
- package/dart/lib/src/replica_readiness.dart +200 -50
- package/dart/lib/src/runner/node-entry.mjs +2481 -1022
- package/dart/lib/src/sync_status_projection.dart +346 -4
- package/dart/lib/src/sync_status_reducer.dart +44 -0
- package/dart/lib/src/test_harness.dart +1 -0
- package/dart/lib/src/typed_watch_registry.dart +67 -7
- package/dart/pubspec.lock +1 -1
- package/dart/pubspec.yaml +2 -2
- package/dart/test/copy_repair_status_test.dart +238 -0
- package/dart/test/reads_never_wait_test.dart +387 -0
- package/dart/test/replica_readiness_test.dart +223 -109
- package/dart/test/sync_status_reducer_test.dart +39 -0
- package/dart/test/typed_watch_registry_test.dart +69 -0
- package/dart/test/unshared_work_status_test.dart +269 -0
- package/dart/test/workspace_preparation_upkeep_test.dart +40 -6
- package/dart_types/CHANGELOG.md +18 -0
- package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +320 -85
- package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pb.dart +57 -1
- package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pb.dart +14 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pbenum.dart +25 -1
- package/dart_types/lib/src/runtime_outcome.dart +17 -0
- package/dart_types/pubspec.yaml +1 -1
- package/package.json +5 -2
- package/src/codegen_ts.ts +7 -8
- package/src/compile_package_main.ts +11 -4
- package/src/reception_unit_artifact.ts +105 -0
- package/src/reception_unit_artifact_contract.ts +220 -0
- package/src/reception_unit_context.ts +47 -0
- package/src/reception_unit_source.ts +205 -0
|
Binary file
|
package/compile_proof.mjs
CHANGED
|
@@ -94,6 +94,21 @@ function dependencyPackageFiles(dslDir, cwd) {
|
|
|
94
94
|
return files;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
function receptionRuntimeFiles(dslDir) {
|
|
98
|
+
// Reception's esbuild graph is loaded outside the compiler's TS import hooks.
|
|
99
|
+
// Match its compiler-owned resolution, rather than a caller's runtime package.
|
|
100
|
+
// Even tree-shaken protocol text affects esbuild's identifier frequencies.
|
|
101
|
+
try {
|
|
102
|
+
const request = createRequire(pathToFileURL(path.join(dslDir, "src", "reception_template.ts")));
|
|
103
|
+
const manifest = request.resolve("@githolon/runtime/package.json");
|
|
104
|
+
return [manifest, ...walk(path.join(path.dirname(manifest), "src"),
|
|
105
|
+
(file) => INPUT_EXTENSIONS.has(path.extname(file)))];
|
|
106
|
+
} catch {
|
|
107
|
+
// A removed dependency changes the inventory; the compiler reports its absence on a miss.
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
97
112
|
/**
|
|
98
113
|
* The authored module a running compile imported, or undefined for compiler and dependency code.
|
|
99
114
|
* A project may name modules outside its own directory (`../../domains/x.ts`); walking the project
|
|
@@ -141,6 +156,7 @@ export function compileInputs({ cwd, dslDir, configPath, argumentIdentity, impor
|
|
|
141
156
|
path.join(dslDir, "operation_contract_check.mjs"),
|
|
142
157
|
path.join(dslDir, "assets", "operation-contract.wasm"),
|
|
143
158
|
...dependencyPackageFiles(dslDir, cwd),
|
|
159
|
+
...receptionRuntimeFiles(dslDir),
|
|
144
160
|
]);
|
|
145
161
|
return {
|
|
146
162
|
authoring,
|
package/dart/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,48 @@
|
|
|
1
|
+
## 0.70.11
|
|
2
|
+
|
|
3
|
+
- 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).
|
|
4
|
+
|
|
5
|
+
## 0.70.10
|
|
6
|
+
|
|
7
|
+
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.
|
|
8
|
+
|
|
9
|
+
- Release with the runtime whose held reads repay their own workspace's index first (CAP-890).
|
|
10
|
+
|
|
11
|
+
- Release with the runtime whose local reads answer from the last-ready read index and type a successor-law refresh as a retryable unavailable (CAP-891).
|
|
12
|
+
|
|
13
|
+
- Release with the runtime whose cold read-index rebuild keeps the last-ready frontier serving reads until the rebuilt index switches in (CAP-900).
|
|
14
|
+
|
|
15
|
+
- A one-shot read whose workspace's read index is being rebuilt under the law its head just adopted waits for the rebuilt index (re-asking near the runtime's retryAfter) instead of throwing `NomosUnavailable(unknown, ...)`. On Jack's phone (1.66.17) `upsertUserEstateAccess` failed this way right after the Home pulled the 2026.10.2 law (CAP-900).
|
|
16
|
+
|
|
17
|
+
- Release with the runtime whose checkpoint transfers are pipelined and run two at a time, with the checkpoint wait limit for remounts (CAP-901).
|
|
18
|
+
|
|
19
|
+
- Reads never wait on the network (CAP-908). A one-shot application data read (rows, a page, by-id, by-type or spatial rows, a count, a sum, and their historic forms) of a workspace this device does not hold yet, is still opening, or is re-indexing under a law this app cannot decode, now answers at once: an empty result, with the session's `SyncStatus.localCopy` and `ApplicationStatus.localCopy` set to `LocalCopyReadiness.arriving` or `.updating` (`ApplicationStatus.localCopyArriving`, `localCopyUpdating`, `localCopyPending`). A read that answers sets it back to `ready`. A framework read with no empty form (child workspaces) reports the typed `NomosUnavailable` at once. `nmsUntilReplicaReady`, which re-asked such reads for up to 10 minutes, is gone. On Jack's iPhone (TestFlight 1334, 11 Oct 2026) a never-fetched data profile's read was held 11 s behind a full-panel spinner.
|
|
20
|
+
- A watch on such a workspace emits the empty result and the status at once, then registers in the background (woken by the workspace's sync) and emits the rows; it stops re-asking as soon as the peer delivers its first result.
|
|
21
|
+
|
|
22
|
+
- `LocalCopyReadiness.firstDownload` and `ApplicationStatus.localCopyFirstDownload` (CAP-913). A read of a workspace that has never been on this device answers empty at once, and says so: `firstDownload`, not merely `arriving`. Once the copy lands, the status is `arriving` while its indexes are prepared, then `ready` when a read answers from it. Admission's raw frontier index is built before the read model, so `ready` also means a write will not wait on an index. `localCopyArriving` stays true for both `firstDownload` and `arriving`. A write to such a workspace throws `NomosUnavailable(firstDownload)` at once, asked once.
|
|
23
|
+
|
|
24
|
+
- `LocalCopyReadiness.repairing` (CAP-920): a workspace's copy on this device is damaged and being repaired (its local Git history is incomplete, it cannot be read, or it is being refetched). Derived from a read answering `localReplicaNotReady` with such a reason and from a sync round failing because a commit of the copy is unreadable; held per workspace until a read answers from the copy again or a refetch replaces it. `SyncStatus.repairingCopies` / `ApplicationStatus.repairingCopies` list every such workspace with when it was first seen, `ApplicationStatus.localCopyRepairing` says it of a session, and `ApplicationStatus.workspace` names a session's workspace. A completed sync round no longer resets a session's `localCopy`. Status only.
|
|
25
|
+
|
|
26
|
+
## 0.70.9
|
|
27
|
+
|
|
28
|
+
Carry the runtime and held-law checkpoint protocol alongside sibling law supply in the hosted-client composition.
|
|
29
|
+
|
|
30
|
+
## 0.70.8
|
|
31
|
+
|
|
32
|
+
- Refresh the bundled Peer Office runtime identity alongside the hosted SDK closure-hint release. Authority, signatures and runtime behavior are unchanged.
|
|
33
|
+
|
|
34
|
+
## 0.70.7
|
|
35
|
+
|
|
36
|
+
- Release the bundled peer runner alongside the fixed npm runtime release for held-unit source custody reads.
|
|
37
|
+
|
|
38
|
+
## 0.70.6
|
|
39
|
+
|
|
40
|
+
- Refresh the bundled peer runtime identity for the framework release that adds per-unit hosted Reception artifacts.
|
|
41
|
+
|
|
42
|
+
## 0.70.5
|
|
43
|
+
|
|
44
|
+
- Release the generated runner restaged with the CAP-888 compiler and CLI package versions.
|
|
45
|
+
|
|
1
46
|
## 0.70.4
|
|
2
47
|
|
|
3
48
|
- Refresh the bundled Node runner alongside the fixed npm-group release for deterministic compiler Reception encoding. Give the changed runner a new immutable client package version.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import 'sync_status_projection.dart'
|
|
1
|
+
import 'sync_status_projection.dart'
|
|
2
|
+
show LocalCopyReadiness, UnsharedSyncWork, WorkspaceFollowStatus;
|
|
2
3
|
|
|
3
4
|
/// Read-only presentation of one Peer Office workspace handle.
|
|
4
5
|
enum ApplicationStatusPhase {
|
|
@@ -33,10 +34,64 @@ final class ApplicationStatus {
|
|
|
33
34
|
this.preparingWorkspaces = 0,
|
|
34
35
|
this.retryingWorkspaces = 0,
|
|
35
36
|
this.preparationActivity,
|
|
37
|
+
this.localCopy = LocalCopyReadiness.ready,
|
|
38
|
+
this.repairingCopies = const {},
|
|
39
|
+
this.unsharedWork = const {},
|
|
40
|
+
this.workspace,
|
|
36
41
|
});
|
|
37
42
|
|
|
43
|
+
/// Every workspace with sync work owed between this device and its server: changes made here
|
|
44
|
+
/// that have not reached it, or the server's changes not yet taken in here. A round that did
|
|
45
|
+
/// nothing does not clear it. Status only: a presentation never counts these as in sync, says
|
|
46
|
+
/// so calmly ("N changes on this device haven't reached the server yet — retrying"), and turns
|
|
47
|
+
/// to attention once one has been owed for a few minutes ([UnsharedSyncWork.since]).
|
|
48
|
+
final Map<String, UnsharedSyncWork> unsharedWork;
|
|
49
|
+
|
|
50
|
+
/// This session's own workspace has work owed to or from its server.
|
|
51
|
+
bool get hasUnsharedWork =>
|
|
52
|
+
workspace != null ? unsharedWork.containsKey(workspace) : unsharedWork.isNotEmpty;
|
|
53
|
+
|
|
54
|
+
/// The workspace a session's status describes; null on the application's own status.
|
|
55
|
+
final String? workspace;
|
|
56
|
+
|
|
38
57
|
final ApplicationStatusPhase phase;
|
|
39
58
|
|
|
59
|
+
/// Whether this workspace's reads answer from a copy on this device. Reads never wait for a copy
|
|
60
|
+
/// to arrive: while it is [LocalCopyReadiness.arriving] or [LocalCopyReadiness.updating] they
|
|
61
|
+
/// answer empty at once and answer again (watches emit again) when it is here.
|
|
62
|
+
final LocalCopyReadiness localCopy;
|
|
63
|
+
|
|
64
|
+
/// This workspace is not readable on this device yet (its first download, or a copy here whose
|
|
65
|
+
/// indexes are still being prepared); its download runs in the background as the application's
|
|
66
|
+
/// own work. Its reads answered empty, not "none".
|
|
67
|
+
bool get localCopyArriving =>
|
|
68
|
+
localCopy == LocalCopyReadiness.arriving ||
|
|
69
|
+
localCopy == LocalCopyReadiness.firstDownload;
|
|
70
|
+
|
|
71
|
+
/// This workspace has never been on this device and its first download is under way: writes to
|
|
72
|
+
/// it are refused at once (NomosUnavailableReason.firstDownload). One-time per workspace per
|
|
73
|
+
/// device. Once the copy lands this is false, [localCopyArriving] stays true while its indexes are
|
|
74
|
+
/// prepared, and editing is ready when [localCopy] is [LocalCopyReadiness.ready].
|
|
75
|
+
bool get localCopyFirstDownload =>
|
|
76
|
+
localCopy == LocalCopyReadiness.firstDownload;
|
|
77
|
+
|
|
78
|
+
/// This workspace's copy is being re-indexed under a newer law; its reads answered empty for now.
|
|
79
|
+
bool get localCopyUpdating => localCopy == LocalCopyReadiness.updating;
|
|
80
|
+
|
|
81
|
+
/// Reads of this workspace answered empty because its copy is not readable here yet.
|
|
82
|
+
bool get localCopyPending => localCopy != LocalCopyReadiness.ready;
|
|
83
|
+
|
|
84
|
+
/// This workspace's copy on this device is damaged and being repaired (its local Git history is
|
|
85
|
+
/// incomplete, it cannot be read, or it is being refetched). You can look around what it shows;
|
|
86
|
+
/// writes to it cannot be admitted until it is repaired. Never "in sync", whatever its sync
|
|
87
|
+
/// rounds report.
|
|
88
|
+
bool get localCopyRepairing => localCopy == LocalCopyReadiness.repairing;
|
|
89
|
+
|
|
90
|
+
/// Every workspace whose copy on this device is damaged and being repaired, with when this peer
|
|
91
|
+
/// first saw it so (the same on the application's status and on each session's). Status only: a
|
|
92
|
+
/// presentation does not count these as in sync and holds editing back for them.
|
|
93
|
+
final Map<String, DateTime> repairingCopies;
|
|
94
|
+
|
|
40
95
|
/// Null counts have not been reported by the runtime; they are not zero.
|
|
41
96
|
final int? localChanges;
|
|
42
97
|
final int? availableChanges;
|
|
@@ -76,5 +131,6 @@ final class ApplicationStatus {
|
|
|
76
131
|
bool get isCurrent =>
|
|
77
132
|
phase == ApplicationStatusPhase.current &&
|
|
78
133
|
localChanges == 0 &&
|
|
79
|
-
availableChanges == 0
|
|
134
|
+
availableChanges == 0 &&
|
|
135
|
+
!hasUnsharedWork;
|
|
80
136
|
}
|
|
@@ -15,6 +15,7 @@ abstract interface class NmsPeerApplicationStreams {
|
|
|
15
15
|
String workspace,
|
|
16
16
|
);
|
|
17
17
|
Stream<void> get childWorkspaces;
|
|
18
|
+
|
|
18
19
|
/// Any accepted local or remote change which may invalidate a generated
|
|
19
20
|
/// application projection. Interpretation remains in generated law-aware code.
|
|
20
21
|
Stream<void> get workspaceChanges;
|
|
@@ -31,17 +32,22 @@ final class NmsPeerApplicationStreamView implements NmsPeerApplicationStreams {
|
|
|
31
32
|
const NmsPeerApplicationStreamView({
|
|
32
33
|
required this.session,
|
|
33
34
|
required this.statusProjection,
|
|
35
|
+
this.sessionStatusProjection,
|
|
34
36
|
this.workspaceOpenProgressSource,
|
|
35
37
|
});
|
|
36
38
|
|
|
37
39
|
final NomosSession session;
|
|
38
40
|
final ApplicationStatus Function(SyncStatus status) statusProjection;
|
|
41
|
+
|
|
42
|
+
/// The projection of this session's own status (it names the session's workspace); defaults to
|
|
43
|
+
/// [statusProjection].
|
|
44
|
+
final ApplicationStatus Function(SyncStatus status)? sessionStatusProjection;
|
|
39
45
|
final Stream<ApplicationWorkspaceOpenProgress> Function(String workspace)?
|
|
40
46
|
workspaceOpenProgressSource;
|
|
41
47
|
|
|
42
48
|
@override
|
|
43
49
|
Stream<ApplicationStatus> get status =>
|
|
44
|
-
session.syncStatus.map(statusProjection);
|
|
50
|
+
session.syncStatus.map(sessionStatusProjection ?? statusProjection);
|
|
45
51
|
|
|
46
52
|
@override
|
|
47
53
|
Stream<ApplicationStatus> get applicationStatus =>
|
|
@@ -68,7 +68,12 @@ import '../application_framework_calls.dart'
|
|
|
68
68
|
import '../kernel_primitives.dart' show NmsKernelPrimitives;
|
|
69
69
|
import '../nomos_trace.dart' show NomosTrace;
|
|
70
70
|
import '../observed_session_directory.dart' show NmsObservedSessionDirectory;
|
|
71
|
-
import '../replica_readiness.dart'
|
|
71
|
+
import '../replica_readiness.dart'
|
|
72
|
+
show
|
|
73
|
+
NmsLocalCopyReport,
|
|
74
|
+
nmsPendingQueryResponse,
|
|
75
|
+
nmsPendingWatchChanged,
|
|
76
|
+
nmsReadNow;
|
|
72
77
|
import '../rows_watch.dart' show NmsRowsMerge, nmsMergeRows;
|
|
73
78
|
import '../operation_receipts.dart'
|
|
74
79
|
show NmsOperationReceiptProjection, NomosOperationReceipt;
|
|
@@ -85,11 +90,17 @@ import 'progress_desk.dart'
|
|
|
85
90
|
import '../signing_identity_kernel.dart' show NmsSigningIdentityKernel;
|
|
86
91
|
import '../sync_status_reducer.dart';
|
|
87
92
|
import '../sync_status_projection.dart'
|
|
88
|
-
show
|
|
93
|
+
show
|
|
94
|
+
LocalCopyReadiness,
|
|
95
|
+
SyncPhase,
|
|
96
|
+
SyncStatus,
|
|
97
|
+
TransferProgress,
|
|
98
|
+
WorkspaceFollowStatus;
|
|
89
99
|
import '../typed_watch_registry.dart' show NmsTypedWatchHandle;
|
|
90
100
|
|
|
91
101
|
export '../sync_status_projection.dart'
|
|
92
102
|
show
|
|
103
|
+
LocalCopyReadiness,
|
|
93
104
|
SyncPhase,
|
|
94
105
|
SyncStatus,
|
|
95
106
|
TransferProgress,
|
|
@@ -964,6 +975,28 @@ class DartReception {
|
|
|
964
975
|
session,
|
|
965
976
|
(current) => reduceSyncCompletion(current, completed, observedAt),
|
|
966
977
|
);
|
|
978
|
+
// A round may "complete" over a damaged copy (nothing pulled, head unchanged) while its
|
|
979
|
+
// kernel cannot read a commit of it; that is reported as a separate failure. Either says
|
|
980
|
+
// the copy is being repaired, and a refetch that replaced it says it is repaired.
|
|
981
|
+
final syncStatuses = _eventPlane._events.syncStatuses;
|
|
982
|
+
final damaged = nmsDamagedCopyOf(completed, session: session);
|
|
983
|
+
if (damaged != null) {
|
|
984
|
+
syncStatuses.markRepairing(damaged, at: observedAt);
|
|
985
|
+
} else if (completed.hasResult() &&
|
|
986
|
+
completed.result.custodyReplaced &&
|
|
987
|
+
completed.result.workspace.isNotEmpty) {
|
|
988
|
+
syncStatuses.markRepaired(completed.result.workspace);
|
|
989
|
+
}
|
|
990
|
+
// Work a round leaves owed stays owed until a round shows progress (CAP-923).
|
|
991
|
+
if (!completed.hasFailure() && completed.hasResult()) {
|
|
992
|
+
final result = completed.result;
|
|
993
|
+
final workspace =
|
|
994
|
+
result.workspace.isNotEmpty ? result.workspace : session;
|
|
995
|
+
if (workspace != null) {
|
|
996
|
+
syncStatuses.observeRound(workspace, result,
|
|
997
|
+
at: observedAt, detail: nmsOwedWorkDetail(result));
|
|
998
|
+
}
|
|
999
|
+
}
|
|
967
1000
|
return;
|
|
968
1001
|
}
|
|
969
1002
|
if (event.hasLawChanged()) {
|
|
@@ -2136,12 +2169,13 @@ class DartReception {
|
|
|
2136
2169
|
/// ledger's verified birth witnesses and lifecycle facts; tenant code only receives safe child links.
|
|
2137
2170
|
///
|
|
2138
2171
|
/// On a cold mount the office answers "not ready yet" while it prepares the parent's birth
|
|
2139
|
-
/// records (CAP-824)
|
|
2140
|
-
///
|
|
2172
|
+
/// records (CAP-824). That answer is reported at once as the typed [NomosUnavailable], never
|
|
2173
|
+
/// waited out and never an empty list: the children are not "none", they are not readable here
|
|
2174
|
+
/// yet, and the session's [SyncStatus.localCopy] says so.
|
|
2141
2175
|
Future<List<nomos_protocol.ChildWorkspace>> nomosFrameworkChildWorkspaces(
|
|
2142
2176
|
{String? namespace}) async {
|
|
2143
2177
|
final session = _runtimeSession();
|
|
2144
|
-
final response = await
|
|
2178
|
+
final response = await nmsReadNow<nomos_protocol.RuntimeResponse>(
|
|
2145
2179
|
() => _invokeRuntimeV2(
|
|
2146
2180
|
nomos_protocol.RuntimeRequest(
|
|
2147
2181
|
listChildWorkspaces: nomos_protocol.ListChildWorkspacesRequest(
|
|
@@ -2150,7 +2184,8 @@ class DartReception {
|
|
|
2150
2184
|
),
|
|
2151
2185
|
),
|
|
2152
2186
|
),
|
|
2153
|
-
|
|
2187
|
+
pending: (_) => null,
|
|
2188
|
+
report: _localCopyReport(session),
|
|
2154
2189
|
);
|
|
2155
2190
|
if (!response.hasChildWorkspaces()) {
|
|
2156
2191
|
throw StateError(
|
|
@@ -2598,6 +2633,36 @@ class DartReception {
|
|
|
2598
2633
|
);
|
|
2599
2634
|
}
|
|
2600
2635
|
|
|
2636
|
+
/// The empty answer of an application data read whose copy is not readable here yet (see
|
|
2637
|
+
/// replica_readiness.dart), as the kernel call response the read decodes. Null when the query
|
|
2638
|
+
/// has no empty form: the typed "not ready" is then reported as it is.
|
|
2639
|
+
nomos_protocol.CallResponse? _pendingKernelCall(
|
|
2640
|
+
nomos_protocol.QueryRequest query,
|
|
2641
|
+
) {
|
|
2642
|
+
final empty = nmsPendingQueryResponse(query);
|
|
2643
|
+
return empty == null ? null : nomos_protocol.CallResponse(query: empty);
|
|
2644
|
+
}
|
|
2645
|
+
|
|
2646
|
+
/// Records what a read found of [session]'s local copy on its sync status, so the application's
|
|
2647
|
+
/// status says "arriving" or "updating" while its reads answer empty, and "ready" once a read
|
|
2648
|
+
/// answers from the copy. Only a change is pushed.
|
|
2649
|
+
NmsLocalCopyReport _localCopyReport(String session) => (readiness) {
|
|
2650
|
+
final projection = _eventPlane._events.syncStatuses;
|
|
2651
|
+
// A session is pinned to its workspace by name. A damaged copy is recorded per workspace
|
|
2652
|
+
// (it is held "repairing" until a read answers from it again); see markRepairing.
|
|
2653
|
+
if (readiness == LocalCopyReadiness.repairing) {
|
|
2654
|
+
projection.markRepairing(session);
|
|
2655
|
+
return;
|
|
2656
|
+
}
|
|
2657
|
+
if (readiness == LocalCopyReadiness.ready)
|
|
2658
|
+
projection.markRepaired(session);
|
|
2659
|
+
if (projection.statusFor(session).localCopy == readiness) return;
|
|
2660
|
+
_updateScopedStatus(
|
|
2661
|
+
session,
|
|
2662
|
+
(current) => current.copyWith(localCopy: readiness),
|
|
2663
|
+
);
|
|
2664
|
+
};
|
|
2665
|
+
|
|
2601
2666
|
Future<nomos_protocol.QueryResponse> _nmsKernelQuery({
|
|
2602
2667
|
required String? session,
|
|
2603
2668
|
required nomos_protocol.QueryAuthority authority,
|
|
@@ -2624,15 +2689,15 @@ class DartReception {
|
|
|
2624
2689
|
// Correlation therefore belongs to the plane just like watches and legacy
|
|
2625
2690
|
// reception calls. Keeping this counter on a child view lets two views mint
|
|
2626
2691
|
// overlapping logical request identities despite sharing one executor.
|
|
2627
|
-
// A copy
|
|
2628
|
-
//
|
|
2692
|
+
// A copy not readable here yet answers at once: empty for an application data
|
|
2693
|
+
// read, with the session's local copy reported arriving or updating, never a wait.
|
|
2629
2694
|
final traceCall = NomosTrace.enabled ? NomosTrace.nextCall() : 0;
|
|
2630
2695
|
final traceBegin = NomosTrace.enabled ? NomosTrace.now() : 0;
|
|
2631
2696
|
if (NomosTrace.enabled) {
|
|
2632
2697
|
NomosTrace.emit('dart', 'call.begin',
|
|
2633
2698
|
'c=$traceCall op=${NomosTrace.describeCall(nomos_protocol.CallRequest(query: query))}');
|
|
2634
2699
|
}
|
|
2635
|
-
final response = await
|
|
2700
|
+
final response = await nmsReadNow<nomos_protocol.CallResponse>(
|
|
2636
2701
|
() {
|
|
2637
2702
|
final requestNumber = ++plane._sequences.request;
|
|
2638
2703
|
if (NomosTrace.enabled) {
|
|
@@ -2656,7 +2721,8 @@ class DartReception {
|
|
|
2656
2721
|
),
|
|
2657
2722
|
);
|
|
2658
2723
|
},
|
|
2659
|
-
|
|
2724
|
+
pending: (_) => _pendingKernelCall(query),
|
|
2725
|
+
report: _localCopyReport(runtimeSession),
|
|
2660
2726
|
);
|
|
2661
2727
|
if (NomosTrace.enabled) {
|
|
2662
2728
|
NomosTrace.emit(
|
|
@@ -2888,7 +2954,7 @@ class DartReception {
|
|
|
2888
2954
|
..authority = authority
|
|
2889
2955
|
..spans = true
|
|
2890
2956
|
..resultFormat = resultFormat;
|
|
2891
|
-
final response = await
|
|
2957
|
+
final response = await nmsReadNow<nomos_protocol.RuntimeResponse>(
|
|
2892
2958
|
() => _invokeRuntimeV2(
|
|
2893
2959
|
nomos_protocol.RuntimeRequest(
|
|
2894
2960
|
historicQuery: nomos_protocol.HistoricQueryRequest(
|
|
@@ -2898,7 +2964,16 @@ class DartReception {
|
|
|
2898
2964
|
),
|
|
2899
2965
|
),
|
|
2900
2966
|
),
|
|
2901
|
-
|
|
2967
|
+
pending: (_) {
|
|
2968
|
+
final empty = _pendingKernelCall(query);
|
|
2969
|
+
return empty == null
|
|
2970
|
+
? null
|
|
2971
|
+
: nomos_protocol.RuntimeResponse(
|
|
2972
|
+
protocolVersion: 2,
|
|
2973
|
+
kernelCall: empty,
|
|
2974
|
+
);
|
|
2975
|
+
},
|
|
2976
|
+
report: _localCopyReport(runtimeSession),
|
|
2902
2977
|
);
|
|
2903
2978
|
if (response.hasFailure()) {
|
|
2904
2979
|
throw StateError('${response.failure.code}: ${response.failure.message}');
|
|
@@ -3221,6 +3296,11 @@ class DartReception {
|
|
|
3221
3296
|
: '',
|
|
3222
3297
|
decode: decode,
|
|
3223
3298
|
stop: _unwatch,
|
|
3299
|
+
// A copy not readable here yet: the watch shows this empty answer at once and the
|
|
3300
|
+
// session's local copy as arriving or updating, then its registration's first result.
|
|
3301
|
+
pending: (subscriptionId) =>
|
|
3302
|
+
nmsPendingWatchChanged(request, subscriptionId),
|
|
3303
|
+
report: _localCopyReport(runtimeSession),
|
|
3224
3304
|
start: (subscriptionId) async {
|
|
3225
3305
|
final response = await _invokeRuntimeV2(
|
|
3226
3306
|
nomos_protocol.RuntimeRequest(
|
|
@@ -127,6 +127,8 @@ final class NmsReceptionSessionView implements NmsReceptionSession {
|
|
|
127
127
|
streams = NmsPeerApplicationStreamView(
|
|
128
128
|
session: desk,
|
|
129
129
|
statusProjection: _applicationStatus,
|
|
130
|
+
sessionStatusProjection: (status) =>
|
|
131
|
+
_applicationStatus(status, workspace: desk.workspace),
|
|
130
132
|
workspaceOpenProgressSource: watchOpenProgressCapability,
|
|
131
133
|
);
|
|
132
134
|
|
|
@@ -143,7 +145,8 @@ final class NmsReceptionSessionView implements NmsReceptionSession {
|
|
|
143
145
|
final Future<void> Function()? closeCapability;
|
|
144
146
|
|
|
145
147
|
@override
|
|
146
|
-
ApplicationStatus get currentStatus =>
|
|
148
|
+
ApplicationStatus get currentStatus =>
|
|
149
|
+
_applicationStatus(desk.currentStatus, workspace: desk.workspace);
|
|
147
150
|
|
|
148
151
|
@override
|
|
149
152
|
Future<void> whenCurrent({required Duration timeout}) =>
|
|
@@ -237,7 +240,9 @@ final class NmsReceptionSessionView implements NmsReceptionSession {
|
|
|
237
240
|
Future<void> close() => closeCapability?.call() ?? Future<void>.value();
|
|
238
241
|
}
|
|
239
242
|
|
|
240
|
-
ApplicationStatus _applicationStatus(SyncStatus status) =>
|
|
243
|
+
ApplicationStatus _applicationStatus(SyncStatus status, {String? workspace}) =>
|
|
244
|
+
ApplicationStatus(
|
|
245
|
+
workspace: workspace,
|
|
241
246
|
phase: switch (status.presentationPhase) {
|
|
242
247
|
SyncPhase.checking => ApplicationStatusPhase.checking,
|
|
243
248
|
SyncPhase.idle => ApplicationStatusPhase.current,
|
|
@@ -267,6 +272,9 @@ ApplicationStatus _applicationStatus(SyncStatus status) => ApplicationStatus(
|
|
|
267
272
|
preparingWorkspaces: status.preparingWorkspaces,
|
|
268
273
|
retryingWorkspaces: status.retryingWorkspaces,
|
|
269
274
|
preparationActivity: status.preparationActivity,
|
|
275
|
+
localCopy: status.localCopy,
|
|
276
|
+
repairingCopies: status.repairingCopies,
|
|
277
|
+
unsharedWork: status.unsharedWork,
|
|
270
278
|
);
|
|
271
279
|
|
|
272
280
|
Future<void> _whenCurrent(DartReception application, Duration timeout) async {
|
|
@@ -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
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
|
|
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
|
-
|
|
95
|
-
|
|
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:
|
|
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
|
-
|
|
112
|
-
|
|
120
|
+
? kernel.gitFrontier
|
|
121
|
+
: previous?.gitFrontier,
|
|
113
122
|
readFrontier: kernel == null
|
|
114
123
|
? null
|
|
115
124
|
: kernel.hasReadFrontier()
|
|
116
|
-
|
|
117
|
-
|
|
125
|
+
? kernel.readFrontier
|
|
126
|
+
: previous?.readFrontier,
|
|
118
127
|
sqliteFrontier: kernel == null
|
|
119
128
|
? null
|
|
120
129
|
: kernel.hasSqliteFrontier()
|
|
121
|
-
|
|
122
|
-
|
|
130
|
+
? kernel.sqliteFrontier
|
|
131
|
+
: previous?.sqliteFrontier,
|
|
123
132
|
);
|
|
124
133
|
}
|
|
125
134
|
|