@githolon/dsl 0.113.1 → 0.113.3

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,8 @@
1
+ ## 0.67.1
2
+
3
+ - Git custody upkeep backing off after a failure is reported as `upkeepRetrying`, not `retrying`: the workspace is
4
+ ready meanwhile, so it no longer shows as a sync problem (CAP-693).
5
+
1
6
  ## 0.67.0
2
7
 
3
8
  - A write sent over the live link has its receipt converge on the custodian's admission: the live desk reports the
@@ -23,6 +23,7 @@ final class WorkspacePreparationReport {
23
23
  this.sqliteFrontier,
24
24
  this.retryAfterMillis,
25
25
  this.stopped = false,
26
+ this.upkeep = false,
26
27
  });
27
28
 
28
29
  final WorkspacePreparationStep step;
@@ -32,7 +33,16 @@ final class WorkspacePreparationReport {
32
33
  final String? sqliteFrontier;
33
34
  final int? retryAfterMillis;
34
35
  final bool stopped;
35
- bool get retrying => retryAfterMillis != null;
36
+
37
+ /// Git custody upkeep after this peer's own writes: it never holds a read or a write.
38
+ final bool upkeep;
39
+
40
+ /// Preparation the workspace is waiting on is backing off after a failure. Upkeep retrying
41
+ /// is not: the workspace is ready meanwhile, and nothing the user can do changes it (CAP-693).
42
+ bool get retrying => retryAfterMillis != null && !upkeep;
43
+
44
+ /// Upkeep backing off after a failure, for diagnostics; never shown as a sync problem.
45
+ bool get upkeepRetrying => retryAfterMillis != null && upkeep;
36
46
 
37
47
  /// Exact equality of reported frontiers, rather than a guess from residency.
38
48
  bool? get sqliteCurrent {
@@ -81,7 +91,8 @@ final class WorkspacePreparationReport {
81
91
  final upkeep = step == WorkspacePreparationStep.preparingCustody ||
82
92
  (kernel == null &&
83
93
  progress.cause ==
84
- protocol.ReadMaintenanceCause.READ_MAINTENANCE_CAUSE_ADMITTED_OFFER);
94
+ protocol.ReadMaintenanceCause
95
+ .READ_MAINTENANCE_CAUSE_ADMITTED_OFFER);
85
96
  return WorkspacePreparationReport(
86
97
  step: step,
87
98
  active: retry == null &&
@@ -91,6 +102,7 @@ final class WorkspacePreparationReport {
91
102
  (kernel == null || kernel.maintenancePending),
92
103
  retryAfterMillis: retry,
93
104
  stopped: progress.stopped,
105
+ upkeep: upkeep,
94
106
  // A new demand may target a newer frontier. Never carry old readiness
95
107
  // across its scheduled observation before the kernel reports a result.
96
108
  gitFrontier: kernel == null
@@ -14587,6 +14587,32 @@ var KernelPort = class {
14587
14587
  // ../src/runtime/placement_desk.ts
14588
14588
  init_define_NOMOS_KERNEL_RELEASE();
14589
14589
 
14590
+ // ../src/runtime/http_origin.ts
14591
+ init_define_NOMOS_KERNEL_RELEASE();
14592
+ var AUTHORITY = /^([a-z][a-z0-9+.-]*):\/\/([^/?#]*)/i;
14593
+ var DEFAULT_PORTS = { http: "80", https: "443" };
14594
+ function httpOrigin(url) {
14595
+ const match = AUTHORITY.exec(url.trim());
14596
+ if (!match) return void 0;
14597
+ const scheme = match[1].toLowerCase();
14598
+ if (scheme !== "http" && scheme !== "https") return void 0;
14599
+ const authority = match[2];
14600
+ const hostAndPort = authority.slice(authority.lastIndexOf("@") + 1);
14601
+ const ipv6 = /^(\[[0-9a-f:.]+\])(?::(\d*))?$/i.exec(hostAndPort);
14602
+ const named = /^([^:[\]\s]+)(?::(\d*))?$/.exec(hostAndPort);
14603
+ const parts = ipv6 ?? named;
14604
+ if (!parts) return void 0;
14605
+ const host = parts[1].toLowerCase();
14606
+ if (host.length === 0 || /[\s<>^`{|}%\\]/.test(host)) return void 0;
14607
+ let port = parts[2] ?? "";
14608
+ if (port !== "") {
14609
+ const value = Number(port);
14610
+ if (!Number.isInteger(value) || value > 65535) return void 0;
14611
+ port = String(value);
14612
+ }
14613
+ return port === "" || port === DEFAULT_PORTS[scheme] ? `${scheme}://${host}` : `${scheme}://${host}:${port}`;
14614
+ }
14615
+
14590
14616
  // ../src/runtime/provider_origins.ts
14591
14617
  init_define_NOMOS_KERNEL_RELEASE();
14592
14618
 
@@ -14926,14 +14952,7 @@ var normalizeProfile = (profile) => {
14926
14952
  ...servedOrigin === void 0 ? {} : { servedOrigin }
14927
14953
  });
14928
14954
  };
14929
- var originOf2 = (url) => {
14930
- try {
14931
- const parsed = new URL(url);
14932
- return parsed.protocol === "https:" || parsed.protocol === "http:" ? parsed.origin : void 0;
14933
- } catch {
14934
- return void 0;
14935
- }
14936
- };
14955
+ var originOf2 = httpOrigin;
14937
14956
  var ReplicaPlacementDirectiveKind = Object.freeze({
14938
14957
  None: "none",
14939
14958
  Cache: "cache",
@@ -20783,7 +20802,10 @@ var ReadMaintenanceDesk = class {
20783
20802
  ...background ? { background } : {},
20784
20803
  ...awaited || existing?.awaited ? { awaited: true } : {},
20785
20804
  failures: existing?.failures ?? 0,
20786
- retryNotBeforeNanoseconds: 0n
20805
+ // A foreground read may retry at once. Upkeep owed by a write or a sync keeps any back-off
20806
+ // it is already serving: otherwise every write retried a failing upkeep immediately, and a
20807
+ // workspace whose upkeep could not succeed retried on every write (CAP-693).
20808
+ retryNotBeforeNanoseconds: isReadDemand ? 0n : existing?.retryNotBeforeNanoseconds ?? 0n
20787
20809
  });
20788
20810
  if (isReadDemand && (!existing?.read || existing.read.owedByAdvance)) {
20789
20811
  const entries = [...this.#pending.entries()];
@@ -21392,18 +21414,17 @@ var SubscriptionManager = class {
21392
21414
  #requestReadMaintenance;
21393
21415
  #clock;
21394
21416
  #subscriptions = /* @__PURE__ */ new Map();
21395
- /** Watches owed a re-read, drained one query group per turn when a turn scheduler is given. */
21417
+ /** Watches owed a re-read, drained one query group per office turn (see [drainOwedTurn]). */
21396
21418
  #owed = /* @__PURE__ */ new Set();
21397
- #nextTurn;
21398
- #draining = false;
21419
+ #deferRefreshes;
21399
21420
  #sequence = 0n;
21400
- constructor(queries, sessions, events, requestReadMaintenance, clock, nextTurn) {
21421
+ constructor(queries, sessions, events, requestReadMaintenance, clock, deferRefreshes = false) {
21401
21422
  this.#queries = queries;
21402
21423
  this.#sessions = sessions;
21403
21424
  this.#events = events;
21404
21425
  this.#requestReadMaintenance = requestReadMaintenance;
21405
21426
  this.#clock = clock;
21406
- this.#nextTurn = nextTurn;
21427
+ this.#deferRefreshes = deferRefreshes;
21407
21428
  }
21408
21429
  start(request, command) {
21409
21430
  if (!command.subscriptionId) throw new Error("watch requires a subscription identity");
@@ -21509,34 +21530,33 @@ var SubscriptionManager = class {
21509
21530
  * or replace current authorization. Each watch still owns its delivery state.
21510
21531
  */
21511
21532
  #refreshMatching(matches, cause) {
21512
- if (this.#nextTurn && !cause) {
21533
+ if (this.#deferRefreshes && !cause) {
21513
21534
  for (const [id, subscription] of this.#subscriptions) {
21514
21535
  if (subscription.request.query && matches(subscription)) this.#owed.add(id);
21515
21536
  }
21516
- this.#drain();
21517
21537
  return;
21518
21538
  }
21519
21539
  this.#refreshGroups(this.#groups(matches), cause);
21520
21540
  }
21521
- #drain() {
21522
- if (this.#draining || this.#owed.size === 0) return;
21523
- this.#draining = true;
21524
- this.#nextTurn(() => {
21525
- this.#draining = false;
21526
- if (this.#owed.size === 0) return;
21527
- const first = this.#owed.values().next().value;
21528
- const lead = this.#subscriptions.get(first);
21529
- if (!lead?.request.query) {
21530
- this.#owed.delete(first);
21531
- } else {
21532
- const declaration = toBinary(QueryRequestSchema, lead.request.query);
21533
- const session = lead.request.session;
21534
- const group = this.#groups((subscription) => subscription.request.session === session && this.#owed.has(subscription.request.subscriptionId) && sameBytes(toBinary(QueryRequestSchema, subscription.request.query), declaration));
21535
- for (const each of group) for (const watch of each.watches) this.#owed.delete(watch.request.subscriptionId);
21536
- this.#refreshGroups(group);
21537
- }
21538
- this.#drain();
21539
- });
21541
+ /** Whether any watch is owed a re-read: the office's next drive() is due at once. */
21542
+ get owesRefresh() {
21543
+ return this.#owed.size > 0;
21544
+ }
21545
+ /** One office turn of owed re-reads: the first owed watch's query group. False when none was owed. */
21546
+ drainOwedTurn() {
21547
+ if (this.#owed.size === 0) return false;
21548
+ const first = this.#owed.values().next().value;
21549
+ const lead = this.#subscriptions.get(first);
21550
+ if (!lead?.request.query) {
21551
+ this.#owed.delete(first);
21552
+ return true;
21553
+ }
21554
+ const declaration = toBinary(QueryRequestSchema, lead.request.query);
21555
+ const session = lead.request.session;
21556
+ const group = this.#groups((subscription) => subscription.request.session === session && this.#owed.has(subscription.request.subscriptionId) && sameBytes(toBinary(QueryRequestSchema, subscription.request.query), declaration));
21557
+ for (const each of group) for (const watch of each.watches) this.#owed.delete(watch.request.subscriptionId);
21558
+ this.#refreshGroups(group);
21559
+ return true;
21540
21560
  }
21541
21561
  #groups(matches) {
21542
21562
  const groups = [];
@@ -21660,6 +21680,39 @@ function createSyntheticRequest(requestId) {
21660
21680
  });
21661
21681
  }
21662
21682
 
21683
+ // ../src/runtime/shell_contract.ts
21684
+ init_define_NOMOS_KERNEL_RELEASE();
21685
+ var SHELL_CONTRACT_V1 = Object.freeze({
21686
+ version: 1,
21687
+ /** Host globals beyond ECMAScript 2022, which every shell supplies. */
21688
+ host: Object.freeze(["TextEncoder", "TextDecoder", "queueMicrotask", "console"]),
21689
+ /** Nomos bridges a native bootstrap installs for the ports it hands to peerOffice.open. */
21690
+ bridges: Object.freeze([
21691
+ "__nomosKernel",
21692
+ "__nomosCredentialVault",
21693
+ "__nomosProfile",
21694
+ "__nomosObservations",
21695
+ "__nomosCustodyStorage",
21696
+ "__nomosPeerDeskStore",
21697
+ "__nomosSyncDiagnostic",
21698
+ "__nomosShellContract"
21699
+ ]),
21700
+ /** Globals the runtime may only probe: used behind their own `typeof` test, never bare. */
21701
+ probed: Object.freeze(["process", "Deno", "Bun", "define"])
21702
+ });
21703
+ var SHELL_CONTRACT_V2 = Object.freeze({ ...SHELL_CONTRACT_V1, version: 2 });
21704
+ var SHELL_CONTRACT_MINIMUM = 1;
21705
+ function declaredShellContract(scope = globalThis) {
21706
+ const declared = scope.__nomosShellContract;
21707
+ return declared === void 0 ? 1 : Number(declared);
21708
+ }
21709
+ function assertShellContract(scope = globalThis) {
21710
+ const declared = declaredShellContract(scope);
21711
+ if (!Number.isInteger(declared) || declared < SHELL_CONTRACT_MINIMUM) {
21712
+ throw new Error(`nomos.runtime.shell_contract_unsupported: this runtime needs shell contract ${SHELL_CONTRACT_MINIMUM} or later; the shell declares ${String(declared)}`);
21713
+ }
21714
+ }
21715
+
21663
21716
  // ../src/runtime/replica_follow_controller.ts
21664
21717
  init_define_NOMOS_KERNEL_RELEASE();
21665
21718
 
@@ -29657,10 +29710,9 @@ var PeerOffice = class {
29657
29710
  this.#trace,
29658
29711
  (session) => this.#readMaintenance.schedule(session, ReadMaintenanceCause.READ_DEBT),
29659
29712
  observations,
29660
- // An interactive peer keeps its thread responsive while watches catch up.
29661
- this.#interactivePeer ? (run2) => {
29662
- setTimeout(run2, 0);
29663
- } : void 0
29713
+ // An interactive peer keeps its thread responsive while watches catch up: owed re-reads
29714
+ // wait for its drive() turns.
29715
+ this.#interactivePeer
29664
29716
  );
29665
29717
  this.#application = new ApplicationDesk({
29666
29718
  onAdmitted: (session) => {
@@ -30067,7 +30119,9 @@ var PeerOffice = class {
30067
30119
  * mirrors queue/backoff state.
30068
30120
  */
30069
30121
  drive() {
30070
- return this.#maintenance.drive();
30122
+ if (this.#subscriptions.drainOwedTurn()) return 0n;
30123
+ const wake = this.#maintenance.drive();
30124
+ return this.#subscriptions.owesRefresh ? 0n : wake;
30071
30125
  }
30072
30126
  /**
30073
30127
  * Carry one exact maintenance demand through a durable host continuation.
@@ -31028,6 +31082,7 @@ var PeerOffice = class {
31028
31082
  };
31029
31083
  var peerOffice = {
31030
31084
  open(kernel, credentials, profile, observations, custodyStorage, peerDeskStore) {
31085
+ assertShellContract();
31031
31086
  return new PeerOffice(kernel, credentials, profile, observations, custodyStorage, peerDeskStore);
31032
31087
  },
31033
31088
  Peer: PeerOffice
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.67.0
10
+ version: 0.67.1
11
11
 
12
12
  environment:
13
13
  sdk: ^3.5.0
@@ -7,7 +7,8 @@ void main() {
7
7
  final packing = WorkspacePreparationReport.fromMaintenance(
8
8
  protocol.ReadMaintenanceProgress(
9
9
  workspace: 'estate',
10
- cause: protocol.ReadMaintenanceCause.READ_MAINTENANCE_CAUSE_ADMITTED_OFFER,
10
+ cause:
11
+ protocol.ReadMaintenanceCause.READ_MAINTENANCE_CAUSE_ADMITTED_OFFER,
11
12
  kernel: protocol.MaintenanceQuantumResponse(
12
13
  phase: protocol.MaintenancePhase.MAINTENANCE_PHASE_GIT_CUSTODY,
13
14
  maintenancePending: true,
@@ -20,7 +21,8 @@ void main() {
20
21
  final scheduled = WorkspacePreparationReport.fromMaintenance(
21
22
  protocol.ReadMaintenanceProgress(
22
23
  workspace: 'estate',
23
- cause: protocol.ReadMaintenanceCause.READ_MAINTENANCE_CAUSE_ADMITTED_OFFER,
24
+ cause:
25
+ protocol.ReadMaintenanceCause.READ_MAINTENANCE_CAUSE_ADMITTED_OFFER,
24
26
  ),
25
27
  );
26
28
  expect(scheduled.active, isFalse);
@@ -35,6 +37,33 @@ void main() {
35
37
  ),
36
38
  ),
37
39
  );
38
- expect(reads.active, isTrue, reason: 'a read index still being built is real preparation');
40
+ expect(reads.active, isTrue,
41
+ reason: 'a read index still being built is real preparation');
42
+ });
43
+
44
+ test('upkeep backing off after a failure is not a sync problem (CAP-693)',
45
+ () {
46
+ // A shallow device's Git upkeep was refused by the kernel; the status turned orange though
47
+ // every read and write still worked.
48
+ final failingUpkeep = WorkspacePreparationReport.fromMaintenance(
49
+ protocol.ReadMaintenanceProgress(
50
+ workspace: 'estate',
51
+ cause:
52
+ protocol.ReadMaintenanceCause.READ_MAINTENANCE_CAUSE_ADMITTED_OFFER,
53
+ retryAfterMillis: 4000,
54
+ ),
55
+ );
56
+ expect(failingUpkeep.retrying, isFalse);
57
+ expect(failingUpkeep.upkeepRetrying, isTrue);
58
+
59
+ final failingReads = WorkspacePreparationReport.fromMaintenance(
60
+ protocol.ReadMaintenanceProgress(
61
+ workspace: 'estate',
62
+ cause: protocol.ReadMaintenanceCause.READ_MAINTENANCE_CAUSE_OPEN,
63
+ retryAfterMillis: 4000,
64
+ ),
65
+ );
66
+ expect(failingReads.retrying, isTrue,
67
+ reason: 'preparation a read waits on still shows');
39
68
  });
40
69
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@githolon/dsl",
3
- "version": "0.113.1",
3
+ "version": "0.113.3",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "Nomos 2 domain-authoring DSL: aggregates + directives in TS, executed and encoded to the Rust kernel's wire shapes.",
@@ -73,7 +73,7 @@
73
73
  "test": "vitest run"
74
74
  },
75
75
  "dependencies": {
76
- "@githolon/runtime": "^0.113.1",
76
+ "@githolon/runtime": "^0.113.3",
77
77
  "@bufbuild/protobuf": "^2.13.0",
78
78
  "@bufbuild/protoc-gen-es": "^2.13.0",
79
79
  "esbuild": "0.24.2",
@@ -2543,8 +2543,8 @@ const NOMOS_TYPES_DEP = "^0.12.0";
2543
2543
  // 0.48.x: session-bound clients (XClient.bind/NomosClientInterface/DomainClientSessionMismatch/
2544
2544
  // DomainNotInstalledForSession, engine.follow, pendingConflicts lanes) — the generated client
2545
2545
  // REQUIRES this surface. 0.48.1 fixes NomosSession.watchPendingConflicts delegation.
2546
- const NOMOS_CLIENT_DEP = "^0.67.0";
2547
- const NOMOS_FLUTTER_DEP = "^0.66.1";
2546
+ const NOMOS_CLIENT_DEP = "^0.67.1";
2547
+ const NOMOS_FLUTTER_DEP = "^0.66.3";
2548
2548
  // DRIFT TRIPWIRE: pubspec_pins.test.ts asserts these equal the CURRENT versions in the Dart and Flutter
2549
2549
  // package pubspecs — a release without this bump fails the suite, so tenants never
2550
2550
  // land back in dependency_overrides (the exact co2 report this closes, third occurrence).