@githolon/dsl 0.103.12 → 0.103.13

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.
Files changed (43) hide show
  1. package/dart/CHANGELOG.md +5 -0
  2. package/dart/lib/src/runner/node-entry.mjs +234 -170
  3. package/dart/lib/src/runner/offline-install.mjs +87 -112
  4. package/dart/lib/src/webview_bridge.dart +11 -11
  5. package/dart/pubspec.yaml +1 -1
  6. package/dart/test/webview_bridge_test.dart +10 -7
  7. package/dart_types/CHANGELOG.md +1 -1
  8. package/dart_types/lib/nomos_types.dart +4 -4
  9. package/dart_types/lib/src/versioned_nomos_ref.dart +13 -11
  10. package/package.json +1 -1
  11. package/runtime/generated-application-host.js +424 -237
  12. package/src/aggregate.ts +3 -1
  13. package/src/build_package.ts +77 -6
  14. package/src/check_package_main.ts +39 -16
  15. package/src/codegen_dart.ts +185 -48
  16. package/src/codegen_dart_application.ts +82 -16
  17. package/src/codegen_dart_client.ts +38 -10
  18. package/src/codegen_dot.ts +3 -3
  19. package/src/codegen_proof.ts +24 -2
  20. package/src/codegen_ts.ts +32 -19
  21. package/src/compile_package_main.ts +68 -11
  22. package/src/derived.ts +17 -0
  23. package/src/directive.ts +65 -0
  24. package/src/engine_entry.ts +22 -2
  25. package/src/enum_type.ts +71 -0
  26. package/src/fields.ts +135 -5
  27. package/src/flow_gate.ts +2 -1
  28. package/src/framework/workspaces.ts +73 -0
  29. package/src/idof_lint.ts +5 -1
  30. package/src/index.ts +20 -0
  31. package/src/kernel_capabilities.ts +12 -1
  32. package/src/law_check.ts +107 -28
  33. package/src/law_graph.ts +151 -20
  34. package/src/law_graph_usda.ts +23 -0
  35. package/src/law_value_type.ts +10 -3
  36. package/src/lsp_server.ts +26 -16
  37. package/src/package_config.ts +112 -9
  38. package/src/package_feature_topology.ts +174 -0
  39. package/src/plan_source_slice.ts +6 -0
  40. package/src/report.ts +6 -0
  41. package/src/stable_ids_for_module.ts +1 -0
  42. package/src/value_object.ts +9 -2
  43. package/src/workspace_sharding.ts +4 -4
@@ -19,6 +19,8 @@ import {
19
19
  introspectPayload,
20
20
  readModelClassName,
21
21
  registerDartNames,
22
+ collectDartEnums,
23
+ dartEnumTypeForField,
22
24
  } from "./codegen_dart.js";
23
25
  import { dartClientClassName } from "./codegen_dart_client.js";
24
26
  import { finishCount } from "./count.js";
@@ -109,7 +111,11 @@ function keyParam(key: string): string {
109
111
  return safe(/^[0-9]/.test(cleaned) ? `v${cleaned}` : cleaned);
110
112
  }
111
113
 
112
- function keyType(aggregate: AggregateHandle | undefined, key: string): string {
114
+ function keyType(
115
+ aggregate: AggregateHandle | undefined,
116
+ key: string,
117
+ enumDartTypes: ReadonlyMap<string, string>,
118
+ ): string {
113
119
  const first = key.includes(".") ? key.split(".")[0]! : key;
114
120
  const field = aggregate?.fields[first] as Field | undefined;
115
121
  if (field?.kind === "int" || field?.kind === "counter") return "int";
@@ -124,6 +130,8 @@ function keyType(aggregate: AggregateHandle | undefined, key: string): string {
124
130
  if (field.refAggregateId !== undefined)
125
131
  return `NomosRef<${concreteClassName(field.refAggregateId)}>`;
126
132
  }
133
+ const enumType = dartEnumTypeForField(field, enumDartTypes);
134
+ if (enumType !== undefined) return enumType;
127
135
  if (
128
136
  field?.kind === "string" &&
129
137
  isSha256DigestSchema(field.zod as z.ZodTypeAny)
@@ -392,6 +400,13 @@ export function generateDartApplication(
392
400
  ): string {
393
401
  registerDartNames(mod.aggregates);
394
402
  const domain = mod.domain ?? mod.name;
403
+ const enumDartTypes = collectDartEnums(
404
+ mod.aggregates,
405
+ mod.directives,
406
+ domain,
407
+ mod.valueObjects ?? [],
408
+ mod.deriveds ?? [],
409
+ ).dartTypes;
395
410
  const application = dartApplicationClassName(domain);
396
411
  const activity = dartApplicationActivityClassName(domain);
397
412
  const client = dartClientClassName(domain);
@@ -434,15 +449,15 @@ export function generateDartApplication(
434
449
  );
435
450
  if (hasVersionedRefs) {
436
451
  claimApplicationMember(
437
- "freshness",
452
+ "versionStatus",
438
453
  "the generated versioned-reference boundary"
439
454
  );
440
455
  appLines.push(
441
456
  ` /// Check whether [reference]'s immutable publication is still current. This reads the`,
442
457
  ` /// stable series' one compiled pointer; it never reconstructs publication history.`,
443
- ` Future<ReferenceFreshness<TVersion>> freshness<TVersion, TSeries>(`,
458
+ ` Future<ReferenceVersionStatus<TVersion>> versionStatus<TVersion, TSeries>(`,
444
459
  ` VersionedNomosRef<TVersion, TSeries> reference) =>`,
445
- ` _client.referenceFreshness(reference);`,
460
+ ` _client.referenceVersionStatus(reference);`,
446
461
  ``
447
462
  );
448
463
  }
@@ -590,10 +605,21 @@ export function generateDartApplication(
590
605
  (input) => input.idField
591
606
  )
592
607
  );
593
- return specs
608
+ const targetBinding = directive.targetBinding;
609
+ const targetChecks = targetBinding === undefined
610
+ ? []
611
+ : [
612
+ ` _nmsRequireReference(target, ${JSON.stringify(
613
+ targetBinding.aggregateId
614
+ )}, _client.session?.workspace ?? '');`,
615
+ ];
616
+ return [
617
+ ...targetChecks,
618
+ ...specs
594
619
  .filter(
595
620
  (spec) =>
596
621
  (spec.toJson === "ref" || spec.toJson === "addressRef") &&
622
+ spec.name !== targetBinding?.field &&
597
623
  !recordInputIds.has(spec.name)
598
624
  )
599
625
  .map((spec) => {
@@ -618,7 +644,8 @@ export function generateDartApplication(
618
644
  targetWire
619
645
  )}, ${expectedWorkspace}));`
620
646
  : ` ${check}`;
621
- });
647
+ }),
648
+ ];
622
649
  };
623
650
 
624
651
  for (const aggregate of mod.aggregates) {
@@ -832,6 +859,9 @@ export function generateDartApplication(
832
859
  // deriving the locator from the typed reference, rather than reaching for
833
860
  // the deliberately removed raw workspace/id fields.
834
861
  const factValue = (path: string): string => {
862
+ if (path === directive.targetBinding?.field) {
863
+ return "NmsInternalNomosRefs.id(target)";
864
+ }
835
865
  if (!path.includes(".")) {
836
866
  const workspaceOwner = foreignInputs.find(
837
867
  (input) => input.workspaceField === path
@@ -850,8 +880,11 @@ export function generateDartApplication(
850
880
  };
851
881
  const trust = directive.businessSourceTrust;
852
882
  const method = member(directive.id);
883
+ const call = directive.targetBinding === undefined
884
+ ? `_client.${method}(facts)`
885
+ : `_client.${method}(target, facts)`;
853
886
  if (trust === undefined) {
854
- return { prelude: [], call: `_client.${method}(facts)` };
887
+ return { prelude: [], call };
855
888
  }
856
889
  const copied = Object.entries(trust.copy)
857
890
  .sort(([a], [b]) => a.localeCompare(b))
@@ -875,7 +908,7 @@ export function generateDartApplication(
875
908
  ` },`,
876
909
  ` );`,
877
910
  ],
878
- call: `_client.${method}(facts)`,
911
+ call,
879
912
  };
880
913
  };
881
914
 
@@ -923,6 +956,18 @@ export function generateDartApplication(
923
956
  for (const [index, policy] of sharingPolicies.entries()) {
924
957
  const pendingKey = policy.pending.key[0]!;
925
958
  const pendingMethod = member(policy.pending.id);
959
+ const pendingAggregate = aggregates.get(policy.pending.returns);
960
+ const pendingField = pendingAggregate?.fields[pendingKey] as
961
+ | Field
962
+ | undefined;
963
+ const pendingValue =
964
+ pendingField?.kind === "enum"
965
+ ? `${keyType(
966
+ pendingAggregate,
967
+ pendingKey,
968
+ enumDartTypes,
969
+ )}.fromWire(${JSON.stringify(policy.pendingValue)})`
970
+ : JSON.stringify(policy.pendingValue);
926
971
  const person = sharingOrderValue(policy, policy.person, "wire");
927
972
  const role = sharingOrderValue(policy, policy.role, "wire");
928
973
  const scope = sharingOrderValue(policy, policy.scope, "wire");
@@ -992,7 +1037,7 @@ export function generateDartApplication(
992
1037
  ``,
993
1038
  ` Future<void> _nmsDrainWorkspaceSharingOnce${index}() async {`,
994
1039
  ` final orders = await _client.${pendingMethod}(`,
995
- ` ${member(pendingKey)}: ${JSON.stringify(policy.pendingValue)},`,
1040
+ ` ${member(pendingKey)}: ${pendingValue},`,
996
1041
  ` );`,
997
1042
  ` for (final order in orders) {`,
998
1043
  ` late final ApplicationShareResult share;`,
@@ -1328,6 +1373,19 @@ export function generateDartApplication(
1328
1373
 
1329
1374
  const method = member(directive.id);
1330
1375
  const payload = dartActionInputClassName(directive);
1376
+ const targetOnly = directive.targetBinding !== undefined &&
1377
+ introspectPayload(directive.payloadSchema as z.ZodTypeAny, new Map()).every(
1378
+ (spec) =>
1379
+ spec.name === directive.targetBinding?.field ||
1380
+ spec.name === directive.businessPrincipalField,
1381
+ ) &&
1382
+ (directive.businessRecordInputs?.length ?? 0) === 0;
1383
+ const targetParameter = directive.targetBinding === undefined
1384
+ ? `${payload} facts`
1385
+ : targetOnly
1386
+ ? `NomosRef<${concreteClassName(directive.targetBinding.aggregateId)}> target`
1387
+ : `NomosRef<${concreteClassName(directive.targetBinding.aggregateId)}> target, ${payload} facts`;
1388
+ const factsPrelude = targetOnly ? [` const facts = ${payload}();`] : [];
1331
1389
  const referenceChecks = actionReferenceChecks(directive);
1332
1390
  const outputs = dartActionOutputs(directive, mod.aggregates);
1333
1391
  const resultClass = dartActionResultClassName(directive.id);
@@ -1379,7 +1437,8 @@ export function generateDartApplication(
1379
1437
  ` /// ${cap(camel(directive.id))}. ${businessActionDoc(directive)}`,
1380
1438
  ` /// Canonical write path: pass business facts here; never mirror this action in application state`,
1381
1439
  ` /// or call a lower-level client.`,
1382
- ` Future<${resultClass}> ${method}(${payload} facts) async {`,
1440
+ ` Future<${resultClass}> ${method}(${targetParameter}) async {`,
1441
+ ...factsPrelude,
1383
1442
  ...referenceChecks,
1384
1443
  ...sourceTrust.prelude,
1385
1444
  ` final change = await ${sourceTrust.call};`,
@@ -1393,7 +1452,8 @@ export function generateDartApplication(
1393
1452
  ` /// ${cap(camel(directive.id))}. ${businessActionDoc(directive)}`,
1394
1453
  ` /// Canonical write path: pass business facts here; never mirror this action in application state`,
1395
1454
  ` /// or call a lower-level client.`,
1396
- ` Future<NomosOperationReceipt> ${method}(${payload} facts) async {`,
1455
+ ` Future<NomosOperationReceipt> ${method}(${targetParameter}) async {`,
1456
+ ...factsPrelude,
1397
1457
  ...referenceChecks,
1398
1458
  ...sourceTrust.prelude,
1399
1459
  ` final change = await ${sourceTrust.call};`,
@@ -1411,7 +1471,8 @@ export function generateDartApplication(
1411
1471
  ` /// ${cap(
1412
1472
  camel(directive.id)
1413
1473
  )}. Returns every affected typed business record reference.`,
1414
- ` Future<${resultClass}> ${method}(${payload} facts) async {`,
1474
+ ` Future<${resultClass}> ${method}(${targetParameter}) async {`,
1475
+ ...factsPrelude,
1415
1476
  ...referenceChecks,
1416
1477
  ...sourceTrust.prelude,
1417
1478
  ` final change = await ${sourceTrust.call};`,
@@ -1422,7 +1483,8 @@ export function generateDartApplication(
1422
1483
  } else {
1423
1484
  addCollectionMember(directive.aggregateId, method, [
1424
1485
  ` /// ${cap(camel(directive.id))}.`,
1425
- ` Future<NomosOperationReceipt> ${method}(${payload} facts) async {`,
1486
+ ` Future<NomosOperationReceipt> ${method}(${targetParameter}) async {`,
1487
+ ...factsPrelude,
1426
1488
  ...referenceChecks,
1427
1489
  ...sourceTrust.prelude,
1428
1490
  ` final change = await ${sourceTrust.call};`,
@@ -1458,7 +1520,7 @@ export function generateDartApplication(
1458
1520
  const model = readModelClassName(query.returns);
1459
1521
  const aggregate = aggregates.get(query.returns);
1460
1522
  const params = query.key
1461
- .map((key) => `required ${keyType(aggregate, key)} ${keyParam(key)}`)
1523
+ .map((key) => `required ${keyType(aggregate, key, enumDartTypes)} ${keyParam(key)}`)
1462
1524
  .join(", ");
1463
1525
  const args = query.key
1464
1526
  .map((key) => `${keyParam(key)}: ${keyParam(key)}`)
@@ -1786,7 +1848,9 @@ export function generateDartApplication(
1786
1848
  for (const count of (mod.counts ?? []).map((raw) => finishCount(raw))) {
1787
1849
  const method = member(count.id);
1788
1850
  const groupType =
1789
- count.by == null ? "" : keyType(aggregates.get(count.of), count.by);
1851
+ count.by == null
1852
+ ? ""
1853
+ : keyType(aggregates.get(count.of), count.by, enumDartTypes);
1790
1854
  addCollectionMember(count.of, method, [
1791
1855
  count.by == null
1792
1856
  ? ` Future<int> ${method}() => _client.${method}();`
@@ -1798,7 +1862,9 @@ export function generateDartApplication(
1798
1862
  for (const sum of (mod.sums ?? []).map((raw) => finishSum(raw))) {
1799
1863
  const method = member(sum.id);
1800
1864
  const groupType =
1801
- sum.by == null ? "" : keyType(aggregates.get(sum.of), sum.by);
1865
+ sum.by == null
1866
+ ? ""
1867
+ : keyType(aggregates.get(sum.of), sum.by, enumDartTypes);
1802
1868
  addCollectionMember(sum.of, method, [
1803
1869
  sum.by == null
1804
1870
  ? ` Future<int> ${method}() => _client.${method}();`
@@ -49,6 +49,8 @@ import {
49
49
  dartBusinessRecordInputs,
50
50
  dartActionInputClassName,
51
51
  dartMemberName,
52
+ collectDartEnums,
53
+ dartEnumTypeForField,
52
54
  } from "./codegen_dart.js";
53
55
  import { finishCount } from "./count.js";
54
56
  import { finishSum } from "./sum.js";
@@ -147,7 +149,11 @@ const dartStr = (s: string) =>
147
149
  `'${s.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
148
150
 
149
151
  /** The Dart param type for a query index key. Aggregate relations keep their canonical typed ref. */
150
- function dartKeyType(agg: AggregateHandle | undefined, key: string): string {
152
+ function dartKeyType(
153
+ agg: AggregateHandle | undefined,
154
+ key: string,
155
+ enumDartTypes: ReadonlyMap<string, string>,
156
+ ): string {
151
157
  const lead = key.includes(".") ? key.split(".")[0]! : key;
152
158
  const f = agg?.fields[lead] as Field | undefined;
153
159
  if (f && (f.kind === "int" || f.kind === "counter")) return "int";
@@ -162,6 +168,8 @@ function dartKeyType(agg: AggregateHandle | undefined, key: string): string {
162
168
  if (f.refAggregateId !== undefined)
163
169
  return `NomosRef<${concreteClassName(f.refAggregateId)}>`;
164
170
  }
171
+ const enumType = dartEnumTypeForField(f, enumDartTypes);
172
+ if (enumType !== undefined) return enumType;
165
173
  if (f?.kind === "string" && isSha256DigestSchema(f.zod as z.ZodTypeAny)) {
166
174
  return "Sha256Digest";
167
175
  }
@@ -181,6 +189,7 @@ function dartKeyExpr(agg: AggregateHandle | undefined, key: string): string {
181
189
  ) {
182
190
  return `${param}.value`;
183
191
  }
192
+ if (field?.kind === "enum") return `${param}.wire`;
184
193
  if (
185
194
  field?.kind === "string" &&
186
195
  isGitObjectIdSchema(field.zod as z.ZodTypeAny)
@@ -325,6 +334,13 @@ export function generateDartBridgeClient(
325
334
  };
326
335
  registerDartNames(mod.aggregates); // `.dartName` overrides for read-model class names in the client.
327
336
  const domain = mod.domain ?? mod.name;
337
+ const enumDartTypes = collectDartEnums(
338
+ mod.aggregates,
339
+ mod.directives,
340
+ domain,
341
+ mod.valueObjects ?? [],
342
+ mod.deriveds ?? [],
343
+ ).dartTypes;
328
344
  assertModelClientMemberAvailable(mod);
329
345
  const describeModel =
330
346
  opts.describeModel ?? buildNomosDescribeModel([mod], domain);
@@ -457,9 +473,9 @@ export function generateDartBridgeClient(
457
473
  methods.push(
458
474
  ` /// Check only whether an immutable publication pin is still the stable series' current`,
459
475
  ` /// publication. The kernel reads one compiled current pointer; it does not resolve history.`,
460
- ` Future<ReferenceFreshness<TVersion>> referenceFreshness<TVersion, TSeries>(`,
476
+ ` Future<ReferenceVersionStatus<TVersion>> referenceVersionStatus<TVersion, TSeries>(`,
461
477
  ` VersionedNomosRef<TVersion, TSeries> reference) =>`,
462
- ` bridge.checkReferenceFreshness(reference);`,
478
+ ` bridge.checkReferenceVersion(reference);`,
463
479
  ``
464
480
  );
465
481
  }
@@ -578,6 +594,13 @@ export function generateDartBridgeClient(
578
594
  }
579
595
  for (const d of dirs) {
580
596
  const payloadClass = dartActionInputClassName(d);
597
+ const targetBinding = isBusinessIntent(d) ? d.targetBinding : undefined;
598
+ const targetType = targetBinding === undefined
599
+ ? undefined
600
+ : `NomosRef<${concreteClassName(targetBinding.aggregateId)}>`;
601
+ const actionParameters = targetBinding === undefined
602
+ ? `${payloadClass} payload`
603
+ : `${targetType} target, ${payloadClass} payload`;
581
604
  const stamps = autoStampFields(
582
605
  (d as unknown as { payloadSchema: z.ZodTypeAny }).payloadSchema
583
606
  );
@@ -628,6 +651,9 @@ export function generateDartBridgeClient(
628
651
  ` }`,
629
652
  ];
630
653
  const payloadJsonArgs = [
654
+ ...(targetBinding === undefined
655
+ ? []
656
+ : ["nmsInternalTarget: target"]),
631
657
  ...(principalField === undefined
632
658
  ? []
633
659
  : ["nmsInternalPrincipal: nmsInternalPrincipal"]),
@@ -702,7 +728,7 @@ export function generateDartBridgeClient(
702
728
  : "Encrypted field(s) are SEALED via the bound [keyStore] before dispatch (pass `seal: false` to skip)."
703
729
  }${stampsDoc}`,
704
730
  ...(gateDoc ? [gateDoc] : []),
705
- ` Future<NomosOfferOutcome> ${method}(${payloadClass} payload${
731
+ ` Future<NomosOfferOutcome> ${method}(${actionParameters}${
706
732
  ownedSeal ? "" : ", {bool seal = true}"
707
733
  }) async {`,
708
734
  ...(gateGuard ? [gateGuard] : []),
@@ -734,7 +760,7 @@ export function generateDartBridgeClient(
734
760
  methods.push(
735
761
  ` /// Author \`${domain}/${d.id}\` locally under the installed law; returns the typed [NomosOfferOutcome].${createsDoc}${stampsDoc}`,
736
762
  ...(gateDoc ? [gateDoc] : []),
737
- ` Future<NomosOfferOutcome> ${method}(${payloadClass} payload) {`,
763
+ ` Future<NomosOfferOutcome> ${method}(${actionParameters}) {`,
738
764
  ...(gateGuard ? [gateGuard] : []),
739
765
  ...principalPrelude,
740
766
  ` final data = ${payloadJson};`,
@@ -750,7 +776,7 @@ export function generateDartBridgeClient(
750
776
  methods.push(
751
777
  ` /// Author \`${domain}/${d.id}\` locally under the installed law; returns the typed [NomosOfferOutcome].${createsDoc}`,
752
778
  gateDoc,
753
- ` Future<NomosOfferOutcome> ${method}(${payloadClass} payload) {`,
779
+ ` Future<NomosOfferOutcome> ${method}(${actionParameters}) {`,
754
780
  ...(gateGuard ? [gateGuard] : []),
755
781
  ...principalPrelude,
756
782
  ` return _offer(${dartStr(d.id)}, ${payloadJson});`,
@@ -761,7 +787,7 @@ export function generateDartBridgeClient(
761
787
  methods.push(
762
788
  ` /// Author \`${domain}/${d.id}\` locally under the installed law; returns the typed [NomosOfferOutcome].${createsDoc}`,
763
789
  ...(gateDoc ? [gateDoc] : []),
764
- ` Future<NomosOfferOutcome> ${method}(${payloadClass} payload) =>`,
790
+ ` Future<NomosOfferOutcome> ${method}(${actionParameters}) =>`,
765
791
  ` _offer(${dartStr(d.id)}, ${payloadJson});`,
766
792
  ``
767
793
  );
@@ -993,7 +1019,7 @@ export function generateDartBridgeClient(
993
1019
  const model = readModelClassName(q.returns);
994
1020
  const agg = aggById.get(q.returns);
995
1021
  const params = q.key
996
- .map((k) => `required ${dartKeyType(agg, k)} ${dartKeyParam(k)}`)
1022
+ .map((k) => `required ${dartKeyType(agg, k, enumDartTypes)} ${dartKeyParam(k)}`)
997
1023
  .join(", ");
998
1024
  const argMap = `{${q.key
999
1025
  .map((k) => `${dartStr(k)}: ${dartKeyExpr(agg, k)}`)
@@ -1119,7 +1145,8 @@ export function generateDartBridgeClient(
1119
1145
  for (const c of (mod.counts ?? []).map((raw) => finishCount(raw))) {
1120
1146
  const m = safeMethod(lcFirst(camel(c.id)));
1121
1147
  const aggregate = aggById.get(c.of);
1122
- const groupType = c.by == null ? "" : dartKeyType(aggregate, c.by);
1148
+ const groupType =
1149
+ c.by == null ? "" : dartKeyType(aggregate, c.by, enumDartTypes);
1123
1150
  const groupValue = c.by == null ? "" : dartKeyExpr(aggregate, c.by);
1124
1151
  methods.push(
1125
1152
  c.by != null
@@ -1155,7 +1182,8 @@ export function generateDartBridgeClient(
1155
1182
  for (const s of (mod.sums ?? []).map((raw) => finishSum(raw))) {
1156
1183
  const m = safeMethod(lcFirst(camel(s.id)));
1157
1184
  const aggregate = aggById.get(s.of);
1158
- const groupType = s.by == null ? "" : dartKeyType(aggregate, s.by);
1185
+ const groupType =
1186
+ s.by == null ? "" : dartKeyType(aggregate, s.by, enumDartTypes);
1159
1187
  const groupValue = s.by == null ? "" : dartKeyExpr(aggregate, s.by);
1160
1188
  methods.push(
1161
1189
  s.by != null
@@ -39,7 +39,7 @@ import type { AggregateHandle } from "./aggregate.js";
39
39
  import type { Field, FieldKind } from "./fields.js";
40
40
  import type { Directive } from "./directive.js";
41
41
  import type { RelationDecl } from "./relation.js";
42
- import type { DerivedDecl } from "./derived.js";
42
+ import type { AnyDerivedDecl } from "./derived.js";
43
43
  import type { CombinedDecl } from "./combined.js";
44
44
  import type { DomainModule } from "./codegen_dart.js";
45
45
 
@@ -224,7 +224,7 @@ function contextOf(
224
224
  function aggregateLabel(
225
225
  agg: AggregateHandle,
226
226
  border: string,
227
- deriveds: readonly DerivedDecl[],
227
+ deriveds: readonly AnyDerivedDecl[],
228
228
  combineds: readonly CombinedDecl[],
229
229
  showInvariant: boolean,
230
230
  ): string {
@@ -299,7 +299,7 @@ export function emitDomainDot(mod: DomainModule, opts: DotOptions = {}): string
299
299
  const knownIds = new Set(aggregates.map((a) => a.id));
300
300
 
301
301
  // derived/combined read fields grouped by owner aggregate type.
302
- const derivedsByType = new Map<string, DerivedDecl[]>();
302
+ const derivedsByType = new Map<string, AnyDerivedDecl[]>();
303
303
  for (const d of mod.deriveds ?? []) {
304
304
  const list = derivedsByType.get(d.of) ?? [];
305
305
  list.push(d);
@@ -3343,9 +3343,9 @@ function generateSealedApplicationProof(
3343
3343
  ` ? (sample: { readonly operation: string; readonly name: string; readonly durationMs: number; readonly domain?: string; readonly directive?: string }) =>`,
3344
3344
  ` console.error("[nomos-application-performance] " + JSON.stringify(sample))`,
3345
3345
  ` : undefined;`,
3346
- `const coldOpenStarted = performance.now();`,
3346
+ `const commissionStarted = performance.now();`,
3347
3347
  `const application = await ${opener}({ cloud, application: applicationName, signIn, onPerformance: reportPerformance });`,
3348
- `if (reportPerformance) reportPerformance({ operation: "open", name: "generated application ready (cold)", durationMs: performance.now() - coldOpenStarted });`,
3348
+ `if (reportPerformance) reportPerformance({ operation: "open", name: "first application commissioning", durationMs: performance.now() - commissionStarted });`,
3349
3349
  `ok("opened the generated ${applicationName}; identity, custody, recovery and sync stay inside Nomos");`,
3350
3350
  `const waitUntilSaved = async (): Promise<void> => {`,
3351
3351
  ` const current = await application.currentStatus();`,
@@ -3369,8 +3369,17 @@ function generateSealedApplicationProof(
3369
3369
  );
3370
3370
  out.push(
3371
3371
  `const facts = ${JSON.stringify(businessPayload)};`,
3372
+ `const businessBirthStarted = performance.now();`,
3372
3373
  `const born = await application.${applicationBirthMethod!}(facts);`,
3374
+ `reportPerformance?.({ operation: "birth", name: "generated workspace birth e2e", durationMs: performance.now() - businessBirthStarted, domain: ${JSON.stringify(domain)}, directive: ${JSON.stringify(businessBirth.birthDirectiveId)} });`,
3373
3375
  `if (born === undefined) fail("business birth returned no child application");`,
3376
+ `const generatedReadStarted = performance.now();`,
3377
+ `const bornRoots = await born.${generatedAggregateCollectionName(businessBirth.businessAggregateId!)}.all();`,
3378
+ `reportPerformance?.({ operation: "read", name: "generated business read e2e", durationMs: performance.now() - generatedReadStarted, domain: ${JSON.stringify(businessBirth.childDomain)} });`,
3379
+ `if (bornRoots.length === 0) fail("the born application could not read its generated root record");`,
3380
+ `const convergenceStarted = performance.now();`,
3381
+ `await born.creationReceipt;`,
3382
+ `reportPerformance?.({ operation: "sync", name: "generated workspace convergence receipt", durationMs: performance.now() - convergenceStarted, domain: ${JSON.stringify(businessBirth.childDomain)} });`,
3374
3383
  `await waitUntilSaved();`,
3375
3384
  `ok(${JSON.stringify(`birth(${businessBirth.businessAggregateId}) saved through the generated application`)});`,
3376
3385
  );
@@ -3449,7 +3458,9 @@ function generateSealedApplicationProof(
3449
3458
  `if (cleared${proofTypeIdent(field)}?.[${JSON.stringify(field)}] !== null) fail(${JSON.stringify(`CLEAR-LANE PROOF — ${aggregate.id}.${field} did not read back as explicit null`)});`,
3450
3459
  ]);
3451
3460
  out.push(
3461
+ `const businessOfferStarted = performance.now();`,
3452
3462
  `const result = await application.${action.id}(${payloadLiteral});`,
3463
+ `reportPerformance?.({ operation: "offer", name: "generated ordinary offer e2e", durationMs: performance.now() - businessOfferStarted, domain: ${JSON.stringify(domain)}, directive: ${JSON.stringify(action.id)} });`,
3453
3464
  `const created = result.${proofResultMember(aggregate.id)};`,
3454
3465
  `${proofRefSlot(aggregate.id)} = created;`,
3455
3466
  `ok(${JSON.stringify(`${domain}/${action.id} accepted business facts and returned a typed reference`)});`,
@@ -3458,7 +3469,9 @@ function generateSealedApplicationProof(
3458
3469
  `await waitUntilSaved();`,
3459
3470
  ...clearReadLines,
3460
3471
  `const reopened = await ${opener}({ cloud, application: applicationName, signIn });`,
3472
+ `const generatedReadStarted = performance.now();`,
3461
3473
  `const rows = await reopened.${generatedAggregateCollectionName(aggregate.id)}.all();`,
3474
+ `reportPerformance?.({ operation: "read", name: "generated business read e2e", durationMs: performance.now() - generatedReadStarted, domain: ${JSON.stringify(domain)} });`,
3462
3475
  `if (!rows.some((row) => sameRecord(row.ref, created))) fail("a fresh generated application could not read " + created.address + "; restored addresses: " + (rows.map((row) => row.ref.address).join(", ") || "(none)"));`,
3463
3476
  `ok("a fresh generated application read the saved record after synchronization");`,
3464
3477
  );
@@ -3475,6 +3488,15 @@ function generateSealedApplicationProof(
3475
3488
  `const _proofDispose = _proofDisposable[Symbol.asyncDispose];`,
3476
3489
  `if (_proofDispose) await Promise.race([_proofDispose(), new Promise<void>((resolve) => setTimeout(resolve, 1_000))]);`,
3477
3490
  `if (performanceEnabled) {`,
3491
+ ` process.env.NOMOS_FORCE_COLD_APPLICATION_PLANE = "1";`,
3492
+ ` const coldOpenStarted = performance.now();`,
3493
+ ` const coldApplication = await ${opener}({ cloud, application: applicationName, signIn, onPerformance: reportPerformance });`,
3494
+ ` reportPerformance?.({ operation: "open", name: "generated application ready (cold restore)", durationMs: performance.now() - coldOpenStarted });`,
3495
+ ` delete process.env.NOMOS_FORCE_COLD_APPLICATION_PLANE;`,
3496
+ ` const coldStatus = await coldApplication.currentStatus();`,
3497
+ ` if (coldStatus.phase === "attention") fail(coldStatus.message ?? "the cold-restored application needs attention");`,
3498
+ ` const coldDisposable = coldApplication as unknown as { [Symbol.asyncDispose]?: () => Promise<void> };`,
3499
+ ` if (coldDisposable[Symbol.asyncDispose]) await Promise.race([coldDisposable[Symbol.asyncDispose]!(), new Promise<void>((resolve) => setTimeout(resolve, 1_000))]);`,
3478
3500
  ` const warmOpenStarted = performance.now();`,
3479
3501
  ` const warmApplication = await ${opener}({ cloud, application: applicationName, signIn, onPerformance: reportPerformance });`,
3480
3502
  ` reportPerformance?.({ operation: "open", name: "generated application ready (warm)", durationMs: performance.now() - warmOpenStarted });`,
package/src/codegen_ts.ts CHANGED
@@ -55,7 +55,7 @@ import { voMetaOf, collectPayloadValueObjectDescriptors, idOfMetaOf, idOfTargetK
55
55
  import { recursiveTypeBaseName, zodUnwrapLazy } from "./zod_lazy.js";
56
56
  import { finishCount } from "./count.js";
57
57
  import { finishSum } from "./sum.js";
58
- import type { DerivedDecl } from "./derived.js";
58
+ import type { AnyDerivedDecl } from "./derived.js";
59
59
  import type { CombinedDecl } from "./combined.js";
60
60
  import { resolveWorkspaceTypes, type ResolvedWorkspaceType, type WorkspaceTypeDecl } from "./workspace_type.js";
61
61
  import {
@@ -733,7 +733,11 @@ function tsTypeOfReturns(returns: unknown): string {
733
733
  function aggregatesOf(mod: Record<string, unknown>): AggregateHandle[] {
734
734
  const out: AggregateHandle[] = [];
735
735
  for (const v of Object.values(mod)) {
736
- if (v && typeof v === "object" && (v as { __isAggregateHandle?: boolean }).__isAggregateHandle === true) {
736
+ if (
737
+ v && typeof v === "object" &&
738
+ (v as { __isAggregateHandle?: boolean }).__isAggregateHandle === true &&
739
+ (v as { __isForwardRef?: boolean }).__isForwardRef !== true
740
+ ) {
737
741
  out.push(v as AggregateHandle);
738
742
  }
739
743
  }
@@ -1634,27 +1638,27 @@ export function generateTsClient(modules: readonly DomainModule[], opts: TsClien
1634
1638
  `}`,
1635
1639
  ...(hasVersionedRefs
1636
1640
  ? [
1637
- `export type ReferenceFreshness<TVersion> =`,
1641
+ `export type ReferenceVersionStatus<TVersion> =`,
1638
1642
  ` | { readonly status: "current"; readonly latest: NomosRef<TVersion> }`,
1639
- ` | { readonly status: "stale"; readonly latest: NomosRef<TVersion> }`,
1643
+ ` | { readonly status: "outdated"; readonly latest: NomosRef<TVersion> }`,
1640
1644
  ` | { readonly status: "unavailable"; readonly reason: string };`,
1641
1645
  `/** An immutable publication pin plus the stable series which declares its current publication.`,
1642
- ` * freshness() is O(current state): it never reconstructs the pinned record or walks history. */`,
1646
+ ` * versionStatus() is O(current state): it never reconstructs the pinned record or walks history. */`,
1643
1647
  `export interface VersionedNomosRef<TVersion, TSeries> {`,
1644
1648
  ` readonly pinned: NomosRef<TVersion>;`,
1645
1649
  ` readonly series: NomosRef<TSeries>;`,
1646
- ` freshness(): Promise<ReferenceFreshness<TVersion>>;`,
1650
+ ` versionStatus(): Promise<ReferenceVersionStatus<TVersion>>;`,
1647
1651
  `}`,
1648
1652
  `type _NomosRawAddress = { workspace: string; type: string; id: string };`,
1649
1653
  `function _versionedNomosRef<TVersion, TSeries>(nomos: NomosHolon, raw: { pinned: _NomosRawAddress; series: _NomosRawAddress }, currentField: string): VersionedNomosRef<TVersion, TSeries> {`,
1650
1654
  ` const pinned = _nmsRef<TVersion>(raw.pinned.workspace, raw.pinned.type, raw.pinned.id);`,
1651
1655
  ` const series = _nmsRef<TSeries>(raw.series.workspace, raw.series.type, raw.series.id);`,
1652
- ` return { pinned, series, async freshness() {`,
1653
- ` if (nomos.checkRefFreshness === undefined) return { status: "unavailable", reason: "installed Nomos runtime does not support reference freshness" };`,
1654
- ` const result = await nomos.checkRefFreshness([{ pinned: raw.pinned, series: raw.series, currentField }]);`,
1656
+ ` return { pinned, series, async versionStatus() {`,
1657
+ ` if (nomos.checkReferenceVersions === undefined) return { status: "unavailable", reason: "installed Nomos runtime does not support reference version checks" };`,
1658
+ ` const result = await nomos.checkReferenceVersions([{ pinned: raw.pinned, series: raw.series, currentField }]);`,
1655
1659
  ` const outcome = result.unique[0]?.outcome;`,
1656
1660
  ` if (outcome?.status === "current") return { status: "current", latest: pinned };`,
1657
- ` if (outcome?.status === "stale" && outcome.latest !== undefined) return { status: "stale", latest: _nmsRef<TVersion>(outcome.latest.workspace, outcome.latest.type, outcome.latest.id) };`,
1661
+ ` if (outcome?.status === "outdated" && outcome.latest !== undefined) return { status: "outdated", latest: _nmsRef<TVersion>(outcome.latest.workspace, outcome.latest.type, outcome.latest.id) };`,
1658
1662
  ` return { status: "unavailable", reason: String(outcome?.status ?? "unknown") };`,
1659
1663
  ` } };`,
1660
1664
  `}`,
@@ -1794,7 +1798,7 @@ export function generateTsClient(modules: readonly DomainModule[], opts: TsClien
1794
1798
  ...(hasVersionedRefs
1795
1799
  ? [
1796
1800
  ` /** Framework-private O(current-state) version check used by generated VersionedNomosRef. */`,
1797
- ` checkRefFreshness?(refs: readonly { pinned: _NomosRawAddress; series: _NomosRawAddress; currentField: string }[]): Promise<{ unique: { outcome: { status: string; latest?: _NomosRawAddress } }[]; order: number[] }>;`,
1801
+ ` checkReferenceVersions?(refs: readonly { pinned: _NomosRawAddress; series: _NomosRawAddress; currentField: string }[]): Promise<{ unique: { outcome: { status: string; latest?: _NomosRawAddress } }[]; order: number[] }>;`,
1798
1802
  ]
1799
1803
  : []),
1800
1804
  ` /** FRAMEWORK-INTERNAL exact-history adoption seam. Generated clients expose only`,
@@ -2416,9 +2420,11 @@ export function generateTsClient(modules: readonly DomainModule[], opts: TsClien
2416
2420
  // ── read models: one interface per aggregate wire type (deduped), deriveds/combineds merged ──
2417
2421
  const extrasByType = new Map<string, string[]>();
2418
2422
  for (const mod of modules) {
2419
- for (const d of (mod.deriveds ?? []) as DerivedDecl[]) {
2423
+ for (const d of (mod.deriveds ?? []) as AnyDerivedDecl[]) {
2420
2424
  (extrasByType.get(d.of) ?? extrasByType.set(d.of, []).get(d.of)!).push(
2421
- ` /** derived read field (engine-projected) */\n ${d.id}?: ${tsTypeOfReturns(d.returns)};`,
2425
+ "kind" in d && d.kind === "temporalEnum"
2426
+ ? ` /** native temporal enum read field (captured-clock projection) */\n ${d.id}: ${tsTypeOfReturns(d.returns)};`
2427
+ : ` /** derived read field (engine-projected) */\n ${d.id}?: ${tsTypeOfReturns(d.returns)};`,
2422
2428
  );
2423
2429
  }
2424
2430
  for (const c of (mod.combineds ?? []) as CombinedDecl[]) {
@@ -2820,6 +2826,7 @@ export function generateTsClient(modules: readonly DomainModule[], opts: TsClien
2820
2826
  const usedResultNames = new Set<string>();
2821
2827
  const applicationBirths: Array<{
2822
2828
  sourceDomain: string;
2829
+ directiveId: string;
2823
2830
  method: string;
2824
2831
  payloadName: string;
2825
2832
  aggregateId: string;
@@ -3186,6 +3193,7 @@ export function generateTsClient(modules: readonly DomainModule[], opts: TsClien
3186
3193
  const applicationMethod = isBusinessIntent(d) ? d.id : "birth";
3187
3194
  applicationBirths.push({
3188
3195
  sourceDomain: domain,
3196
+ directiveId: d.id,
3189
3197
  method: applicationMethod,
3190
3198
  payloadName,
3191
3199
  aggregateId: businessBirth.aggregateId,
@@ -3206,9 +3214,6 @@ export function generateTsClient(modules: readonly DomainModule[], opts: TsClien
3206
3214
  ...(ownedBirthDescriptor !== undefined
3207
3215
  ? [` if (born.workspace !== _ownedBirthKey.workspace) throw new Error("birth() returned a workspace different from the deterministic owned-custody address");`]
3208
3216
  : []),
3209
- ` await holon.sync?.();`,
3210
- ` if (!holon.settleBirth) throw new Error("birth() requires the current @githolon/client to complete durable recovery");`,
3211
- ` await holon.settleBirth(born.workspace);`,
3212
3217
  ` return _nmsRef<${businessBirth.aggregateId}>(born.workspace, ${JSON.stringify(businessBirth.aggregateId)}, ${JSON.stringify(businessBirth.rootId)});`,
3213
3218
  ` },`,
3214
3219
  );
@@ -3916,7 +3921,7 @@ export function generateTsClient(modules: readonly DomainModule[], opts: TsClien
3916
3921
  `type ${tsDomainApplicationType(domain)} = _NomosPublicDomain<ReturnType<typeof ${tsClientFactoryName(domain)}>>;`
3917
3922
  ),
3918
3923
  ...childApplicationTypes.map((birth) =>
3919
- `export type ${birth.applicationType} = _NomosPublicDomain<ReturnType<typeof ${tsClientFactoryName(birth.targetDomain)}>>;`
3924
+ `export type ${birth.applicationType} = _NomosPublicDomain<ReturnType<typeof ${tsClientFactoryName(birth.targetDomain)}>> & { readonly creationReceipt: Promise<void> };`
3920
3925
  ),
3921
3926
  `type _${applicationName}Business = ${applicationType};`,
3922
3927
  `export type ${applicationName}Status = _NomosApplicationStatus;`,
@@ -3996,11 +4001,19 @@ export function generateTsClient(modules: readonly DomainModule[], opts: TsClien
3996
4001
  ` application[${JSON.stringify(birth.method)}] = async (input: ${birth.payloadName}): Promise<${birth.applicationType}> => {`,
3997
4002
  ` const root = await _nmsRoot${rootIndex}[${JSON.stringify(birth.method)}](input);`,
3998
4003
  ` const address = _nmsReferenceData(root);`,
3999
- ` return nomosInternalOpenGeneratedChildApplication(`,
4004
+ ` const child = await nomosInternalOpenGeneratedChildApplication(`,
4000
4005
  ` holon,`,
4001
4006
  ` address.workspace,`,
4002
4007
  ` (child) => _nomosPublicDomain(${tsClientFactoryName(birth.targetDomain)}(child as NomosHolon)),`,
4003
4008
  ` );`,
4009
+ ` const creationReceipt = (async (): Promise<void> => {`,
4010
+ ` await holon.sync?.();`,
4011
+ ` if (!holon.settleBirth) throw new Error("birth() requires the current @githolon/client to prove remote custody convergence");`,
4012
+ ` await holon.settleBirth(address.workspace);`,
4013
+ ` })();`,
4014
+ ` void creationReceipt.catch(() => {});`,
4015
+ ` Object.defineProperty(child, "creationReceipt", { value: creationReceipt, enumerable: true, configurable: false, writable: false });`,
4016
+ ` return child as ${birth.applicationType};`,
4004
4017
  ` };`,
4005
4018
  ];
4006
4019
  }),
@@ -4061,7 +4074,7 @@ export function generateTsClient(modules: readonly DomainModule[], opts: TsClien
4061
4074
  ` * or parallel cache around it. Nomos owns offline custody and synchronization. */`,
4062
4075
  `export function ${applicationOpener}(options: ${applicationName}OpenOptions): Promise<${applicationName}> {`,
4063
4076
  ` return nomosInternalOpenGeneratedApplication(`,
4064
- ` { cloud: options.cloud, application: options.application, signIn: options.signIn, onPerformance: options.onPerformance, lawHash: ${hashConst} },`,
4077
+ ` { cloud: options.cloud, application: options.application, signIn: options.signIn, onPerformance: options.onPerformance, lawHash: ${hashConst}, readyCommands: [${rootBirths.map((birth) => `{ domain: ${JSON.stringify(birth.sourceDomain)}, packageHash: ${hashConst}, directiveId: ${JSON.stringify(birth.directiveId)} }`).join(", ")}] },`,
4065
4078
  ` (holon, user) => _nomosBind${applicationName}(holon as NomosHolon, user),`,
4066
4079
  ` );`,
4067
4080
  `}`,