@githolon/dsl 0.102.4 → 0.103.0

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 (131) hide show
  1. package/compile_package.mjs +43 -2
  2. package/compile_proof.d.mts +47 -0
  3. package/compile_proof.mjs +343 -0
  4. package/dart/CHANGELOG.md +7 -0
  5. package/dart/lib/src/dev_cloud.dart +8 -8
  6. package/dart/lib/src/nms_generated_runtime.dart +2 -0
  7. package/dart/lib/src/runner/node-entry.mjs +5504 -1236
  8. package/dart/lib/src/runner/offline-install.mjs +4578 -528
  9. package/dart/lib/src/test_harness.dart +206 -152
  10. package/dart/lib/src/webview_bridge.dart +196 -89
  11. package/dart/pubspec.lock +1 -1
  12. package/dart/pubspec.yaml +2 -2
  13. package/dart/test/acquire_session_credentials_test.dart +4 -3
  14. package/dart/test/adv_realm_interference_test.dart +7 -6
  15. package/dart/test/cloud_bound_topology_test.dart +3 -3
  16. package/dart/test/cluster_births_test.dart +4 -3
  17. package/dart/test/cluster_device_keyed_acceptance_test.dart +10 -16
  18. package/dart/test/cluster_local_autosync_test.dart +3 -2
  19. package/dart/test/cluster_scale_test.dart +3 -2
  20. package/dart/test/dlq_session_surface_test.dart +7 -6
  21. package/dart/test/first_app_gauntlet_test.dart +14 -21
  22. package/dart/test/fixtures/wedge/fake-install.mjs +1 -1
  23. package/dart/test/four_layer_nested_topology_test.dart +4 -4
  24. package/dart/test/harness_phase_timeout_test.dart +2 -2
  25. package/dart/test/local_sync_inert_test.dart +1 -1
  26. package/dart/test/parent_signer_recovery_test.dart +2 -2
  27. package/dart/test/sealing_client_test.dart +4 -3
  28. package/dart/test/session_bound_test.dart +8 -7
  29. package/dart/test/test_harness_test.dart +13 -12
  30. package/dart/test/vo_read_typing_test.dart +4 -3
  31. package/dart/test/watch_after_offer_stall_test.dart +4 -3
  32. package/dart/test/webview_bridge_test.dart +102 -5
  33. package/dart_types/CHANGELOG.md +5 -0
  34. package/dart_types/lib/nomos_types.dart +9 -0
  35. package/dart_types/lib/src/nomos_ref.dart +20 -0
  36. package/dart_types/lib/src/optional_ref.dart +85 -0
  37. package/dart_types/lib/src/sha256_digest.dart +41 -0
  38. package/dart_types/lib/src/versioned_nomos_ref.dart +81 -0
  39. package/dart_types/pubspec.yaml +1 -1
  40. package/dart_types/test/optional_ref_test.dart +44 -0
  41. package/dart_types/test/sha256_digest_test.dart +18 -0
  42. package/package.json +9 -5
  43. package/runtime/generated-application-host.js +5365 -1105
  44. package/src/aggregate.ts +18 -0
  45. package/src/application_composition.ts +6 -32
  46. package/src/attested_core.ts +6 -3
  47. package/src/birth_meter.ts +1 -0
  48. package/src/birth_meter_ensure.ts +28 -2
  49. package/src/births.ts +1 -1
  50. package/src/build_package.ts +162 -538
  51. package/src/business_birth.ts +19 -3
  52. package/src/canonical_json.ts +21 -0
  53. package/src/check_package_main.ts +76 -35
  54. package/src/clear_directive.ts +19 -8
  55. package/src/codegen_dart.ts +305 -22
  56. package/src/codegen_dart_application.ts +844 -292
  57. package/src/codegen_dart_client.ts +1084 -407
  58. package/src/codegen_dart_scenario.ts +12 -12
  59. package/src/codegen_proof.ts +169 -39
  60. package/src/codegen_provider_dart.ts +17 -1
  61. package/src/codegen_ts.ts +298 -106
  62. package/src/compat_manifest.ts +2 -2
  63. package/src/compat_package_main.ts +8 -12
  64. package/src/compile_package_main.ts +1866 -597
  65. package/src/compose.ts +1 -1
  66. package/src/directive.ts +208 -15
  67. package/src/engine_entry.ts +165 -111
  68. package/src/evolve_preview.ts +43 -81
  69. package/src/extremum.ts +1 -1
  70. package/src/fields.ts +183 -4
  71. package/src/flow_gate.ts +75 -2
  72. package/src/framework/application_home.ts +2 -0
  73. package/src/framework/auth_provider_law.ts +2 -2
  74. package/src/framework/birth_meter.ts +7 -19
  75. package/src/framework/bootstrap.ts +0 -2
  76. package/src/framework/business_corrections.ts +9 -5
  77. package/src/framework/domain_lifecycle.ts +15 -29
  78. package/src/framework/encryption_scope.ts +2 -1
  79. package/src/framework/framework_aggregates.ts +2 -0
  80. package/src/framework/genesis.ts +16 -14
  81. package/src/framework/home.ts +20 -14
  82. package/src/framework/owned_custody.ts +9 -3
  83. package/src/framework/platform_admin.ts +3 -3
  84. package/src/framework/relations.ts +21 -13
  85. package/src/framework/signing_identity.ts +45 -40
  86. package/src/framework/workspace_invariant.ts +245 -0
  87. package/src/framework/workspaces.ts +23 -29
  88. package/src/index.ts +36 -16
  89. package/src/intent.ts +178 -6
  90. package/src/law_check.ts +32 -11
  91. package/src/law_graph.ts +2902 -0
  92. package/src/law_graph_usda.ts +1799 -0
  93. package/src/law_identity.ts +435 -0
  94. package/src/law_value_type.ts +230 -0
  95. package/src/lsp_server.ts +61 -31
  96. package/src/ops.ts +7 -0
  97. package/src/package_config.ts +141 -0
  98. package/src/package_dependencies.ts +108 -0
  99. package/src/package_dependency_usage.ts +150 -0
  100. package/src/package_source_imports.ts +71 -0
  101. package/src/plan_source_slice.ts +335 -0
  102. package/src/point_guard.ts +192 -0
  103. package/src/precondition.ts +44 -9
  104. package/src/read_projection_namespace.ts +1 -1
  105. package/src/runtime_zod.ts +44 -0
  106. package/src/secondary_ensure.ts +67 -14
  107. package/src/shape_compat.ts +1 -1
  108. package/src/stable_id_identity_bridge.ts +149 -0
  109. package/src/stable_id_lockfile.ts +278 -0
  110. package/src/stable_ids.ts +7 -2
  111. package/src/stable_ids_for_module.ts +348 -0
  112. package/src/stable_ids_types.ts +1 -1
  113. package/src/sum.ts +1 -1
  114. package/src/usd_state.ts +1 -1
  115. package/src/usda_relationship_integrity.ts +66 -0
  116. package/src/usda_text.ts +18 -0
  117. package/src/usdz.ts +78 -0
  118. package/src/value_object.ts +95 -0
  119. package/src/wire.ts +19 -0
  120. package/src/wire_encode.ts +22 -0
  121. package/src/workspace_sharding.ts +52 -36
  122. package/src/workspace_sharing.ts +28 -4
  123. package/src/zod_introspection.ts +1 -1
  124. package/dart/test/cluster_attested_test.dart +0 -201
  125. package/src/compile_engine.ts +0 -595
  126. package/src/manifest.ts +0 -1476
  127. package/src/read_projection_composition.ts +0 -117
  128. package/src/read_projection_manifest.ts +0 -115
  129. package/src/relation_composition.ts +0 -286
  130. package/src/usd.ts +0 -1967
  131. package/src/usd_layers.ts +0 -1776
@@ -85,7 +85,7 @@ export function generateDartScenario(
85
85
  mod: DomainModule,
86
86
  clientPackage: string,
87
87
  testPackage: string,
88
- deployFile: string,
88
+ lawUsdzFile: string,
89
89
  options: {
90
90
  readonly surfaceName?: string;
91
91
  readonly applicationFile?: string;
@@ -194,7 +194,7 @@ export function generateDartScenario(
194
194
  ` final person = signedInAs ?? ${testPerson}.named('test');`,
195
195
  ` final packageFiles = await _applicationPackages();`,
196
196
  ` final environment = await NomosTestHarness.ephemeral(`,
197
- ` deployJsonPaths: packageFiles.map((file) => file.path).toList(growable: false),`,
197
+ ` lawUsdzPaths: packageFiles.map((file) => file.path).toList(growable: false),`,
198
198
  ` ownerSubject: person._principal,`,
199
199
  ` workspace: nmsInternalReceivingGroupNameOf(person._principal),`,
200
200
  ` nodeEntry: Platform.environment['NOMOS_NODE_ENTRY'],`,
@@ -223,13 +223,13 @@ export function generateDartScenario(
223
223
  ` final packageFiles = await _applicationPackages();`,
224
224
  ` final cloud = await NomosTestCloud.start(`,
225
225
  ` serverEntry: serverEntry, platform: applicationName,`,
226
- ` deployJsonPaths: packageFiles.map((file) => file.path).toList(growable: false),`,
226
+ ` lawUsdzPaths: packageFiles.map((file) => file.path).toList(growable: false),`,
227
227
  ` onDiagnostic: Platform.environment['NOMOS_TEST_DIAGNOSTICS'] == '1' ? stderr.writeln : null,`,
228
228
  ` );`,
229
229
  ` late final NomosTestHarness environment;`,
230
230
  ` try {`,
231
231
  ` environment = await NomosTestHarness.cloudBound(`,
232
- ` cloud: cloud, deployJsonPaths: packageFiles.map((file) => file.path).toList(growable: false),`,
232
+ ` cloud: cloud, lawUsdzPaths: packageFiles.map((file) => file.path).toList(growable: false),`,
233
233
  ` platformWorkspace: applicationName, subject: person._principal,`,
234
234
  ` nodeEntry: Platform.environment['NOMOS_NODE_ENTRY'],`,
235
235
  ` phaseTimeout: const Duration(minutes: 2),`,
@@ -314,13 +314,13 @@ export function generateDartScenario(
314
314
  ` final packageAsset = await asset.readAsString();`,
315
315
  ` final identity = RegExp(r'asset\\s+identifier\\s*=\\s*@nomos:package:([^:]+):[0-9a-f]{64}@').firstMatch(packageAsset);`,
316
316
  ` if (identity == null) throw StateError('Application reference is not a compiled Nomos package: \${asset.path}');`,
317
- ` final deploy = File('\${asset.parent.path}\${Platform.pathSeparator}\${identity.group(1)!}.deploy.json');`,
318
- ` if (!deploy.existsSync()) throw StateError('Application package has no deploy body: \${deploy.path}');`,
319
- ` final prior = placements[deploy.path];`,
317
+ ` final law = File('\${asset.parent.path}\${Platform.pathSeparator}\${identity.group(1)!}.law.usdz');`,
318
+ ` if (!law.existsSync()) throw StateError('Application package has no law archive: \${law.path}');`,
319
+ ` final prior = placements[law.path];`,
320
320
  ` if (prior != null && prior != payload) {`,
321
- ` throw StateError('The same package is both a serving reference and deferred payload: \${deploy.path}');`,
321
+ ` throw StateError('The same package is both a serving reference and deferred payload: \${law.path}');`,
322
322
  ` }`,
323
- ` placements[deploy.path] = payload;`,
323
+ ` placements[law.path] = payload;`,
324
324
  ` }`,
325
325
  ` if (placements.isEmpty) throw StateError('The application stage contains no deployable Nomos packages.');`,
326
326
  ` final ordered = placements.entries.toList()`,
@@ -335,15 +335,15 @@ export function generateDartScenario(
335
335
  ` return files;`,
336
336
  ` }`,
337
337
  ` final assetKeys = [`,
338
- ` 'packages/${testPackage}/lib/src/generated/${deployFile}',`,
339
- ` 'lib/src/generated/${deployFile}',`,
338
+ ` 'packages/${testPackage}/lib/src/generated/${lawUsdzFile}',`,
339
+ ` 'lib/src/generated/${lawUsdzFile}',`,
340
340
  ` ];`,
341
341
  ` Object? lastError;`,
342
342
  ` for (final key in assetKeys) {`,
343
343
  ` try {`,
344
344
  ` final data = await rootBundle.load(key);`,
345
345
  ` final dir = await Directory.systemTemp.createTemp('nms-application-package-');`,
346
- ` final file = File('\${dir.path}\${Platform.pathSeparator}${deployFile}');`,
346
+ ` final file = File('\${dir.path}\${Platform.pathSeparator}${lawUsdzFile}');`,
347
347
  ` await file.writeAsBytes(data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes), flush: true);`,
348
348
  ` return [file];`,
349
349
  ` } catch (error) {`,
@@ -45,7 +45,7 @@ import type { WorkspaceInvariantDecl } from "./framework/workspace_invariant.js"
45
45
  import { finishCount } from "./count.js";
46
46
  import { clearDirectiveId } from "./clear_directive.js";
47
47
  import type { DomainInterface } from "./shape_compat.js";
48
- import { idOfMetaOf, idOfTargetKindOf, voMetaOf, voSample } from "./value_object.js";
48
+ import { idOfMetaOf, idOfTargetKindOf, versionedRefOfMetaOf, voMetaOf, voSample } from "./value_object.js";
49
49
  import { mintedCreateField } from "./workspace_routing.js";
50
50
  import { deterministicPorts } from "./ctx.js";
51
51
  import { __setDispatchDomain, __setDispatchContext } from "./framework/relations.js";
@@ -209,6 +209,21 @@ function sampleOfZod(
209
209
  voIncludeOptional = false,
210
210
  visited: ReadonlySet<z.ZodTypeAny> = EMPTY_LAZY_VISITED,
211
211
  ): unknown {
212
+ const versioned = versionedRefOfMetaOf(zt);
213
+ if (versioned !== undefined) {
214
+ return {
215
+ pinned: {
216
+ workspace: "sample-version-workspace",
217
+ type: versioned.targetAggregateType,
218
+ id: "sample-pinned-version",
219
+ },
220
+ series: {
221
+ workspace: "sample-version-workspace",
222
+ type: versioned.seriesAggregateType,
223
+ id: "sample-version-series",
224
+ },
225
+ };
226
+ }
212
227
  // RECURSIVE `z.lazy` (a self-referencing tree/JSON payload): unwrap one level, tracking the
213
228
  // lazy object's IDENTITY on this descent. Re-entering the SAME lazy is a knot: sampling can go
214
229
  // no deeper. The common case terminates NATURALLY one level up (the recursive branch is an
@@ -398,11 +413,25 @@ function typedPayloadLiteral(
398
413
  payload: Readonly<Record<string, unknown>>,
399
414
  referenceFields: Readonly<Record<string, string>>,
400
415
  overrides: Readonly<Record<string, string>> = {},
416
+ optionalReferenceFields: ReadonlySet<string> = new Set(),
401
417
  ): string {
402
- const keys = new Set([...Object.keys(payload), ...Object.keys(overrides)]);
418
+ const keys = new Set([
419
+ ...Object.keys(payload),
420
+ ...Object.keys(overrides),
421
+ ...optionalReferenceFields,
422
+ ]);
403
423
  const entries = [...keys].map((field) => {
404
- const expression = overrides[field]
405
- ?? (referenceFields[field] !== undefined ? `${proofRefGetter(referenceFields[field]!)}()` : JSON.stringify(payload[field]));
424
+ const override = overrides[field];
425
+ const referenceType = referenceFields[field];
426
+ const expression = override !== undefined
427
+ ? override
428
+ : referenceType !== undefined
429
+ ? optionalReferenceFields.has(field)
430
+ ? payload[field] === undefined
431
+ ? "NomosOptionalRef.absent()"
432
+ : `NomosOptionalRef.present(${proofRefGetter(referenceType)}())`
433
+ : `${proofRefGetter(referenceType)}()`
434
+ : JSON.stringify(payload[field]);
406
435
  return `${JSON.stringify(field)}: ${expression}`;
407
436
  });
408
437
  return `{ ${entries.join(", ")} }`;
@@ -728,17 +757,34 @@ export function probeEnsuredTypes(dir: Directive<unknown>, where: string): strin
728
757
  return [...out].sort();
729
758
  }
730
759
 
731
- /** The `status` of a `subjects` setEntry leaf (`writeTuple`/`removeTuple` lowers a JSON `{status, …}` leaf).
732
- * `"active"` = a POSITIVE grant; `"revoked"` = a REVOKE. `undefined` ⇒ not a parseable subject leaf. */
733
- function subjectLeafStatus(op: FieldOp): string | undefined {
734
- if (op.kind !== "setEntry" || op.field !== "subjects" || typeof op.value !== "string") return undefined;
760
+ /** The typed relationship leaf produced by current law, with one legacy string decoder so proof
761
+ * discovery remains able to inspect packages compiled before `RelationshipSubject` became a kernel
762
+ * value. New plans never manufacture the string arm. */
763
+ function subjectLeaf(op: FieldOp): Record<string, unknown> | undefined {
764
+ if (op.kind !== "setEntry" || op.field !== "subjects") return undefined;
765
+ const typed = op.kernelValue !== undefined && "RelationshipSubject" in op.kernelValue
766
+ ? op.kernelValue.RelationshipSubject
767
+ : op.value;
768
+ if (typeof typed === "object" && typed !== null && !Array.isArray(typed)) {
769
+ return typed as Record<string, unknown>;
770
+ }
771
+ if (typeof typed !== "string") return undefined;
735
772
  try {
736
- return (JSON.parse(op.value) as { status?: unknown }).status as string | undefined;
773
+ const parsed = JSON.parse(typed) as unknown;
774
+ return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)
775
+ ? parsed as Record<string, unknown>
776
+ : undefined;
737
777
  } catch {
738
778
  return undefined;
739
779
  }
740
780
  }
741
781
 
782
+ /** `"active"` = a POSITIVE grant; `"revoked"` = a REVOKE. */
783
+ function subjectLeafStatus(op: FieldOp): string | undefined {
784
+ const status = subjectLeaf(op)?.status;
785
+ return typeof status === "string" ? status : undefined;
786
+ }
787
+
742
788
  /**
743
789
  * The relations a directive's plan POSITIVELY GRANTS — judged PER RELATION-TUPLE (per `aggregateId`), never by
744
790
  * relation-name alone. Both a grant (`writeTuple`, `status:"active"`) AND a revoke (`removeTuple`,
@@ -813,10 +859,16 @@ function resolveGrantSubjectFields(
813
859
  const inner = m !== null ? fieldForValue(m[1]) : undefined;
814
860
  if (inner !== undefined) wrappedSubjectFields.add(inner);
815
861
  }
816
- // grantedBy rides the JSON leaf value — scan the serialized leaf for any synthesized payload value.
817
- if (typeof o.value === "string") {
862
+ // grantedBy rides the typed leaf value — match its string fields to synthesized payload values.
863
+ const leaf = subjectLeaf(o);
864
+ if (leaf !== undefined) {
818
865
  for (const [name, sv] of Object.entries(synth.value)) {
819
- if (typeof sv === "string" && sv.length > 0 && name !== fieldForValue(o.entryKey) && o.value.includes(sv)) {
866
+ if (
867
+ typeof sv === "string" &&
868
+ sv.length > 0 &&
869
+ name !== fieldForValue(o.entryKey) &&
870
+ Object.values(leaf).some((value) => value === sv)
871
+ ) {
820
872
  grantedByFields.add(name);
821
873
  }
822
874
  }
@@ -1947,6 +1999,8 @@ export interface ChildBirthLeg {
1947
1999
  * generated client. Present only for an encrypted `.owned()` business birth. */
1948
2000
  readonly businessEncryption?: {
1949
2001
  readonly scope: string;
2002
+ readonly recordType: string;
2003
+ readonly recordId: string;
1950
2004
  readonly namespace: string;
1951
2005
  readonly keyField: string;
1952
2006
  readonly fields: Readonly<Record<string, { readonly aggSid: string; readonly fieldSid: string }>>;
@@ -2101,7 +2155,7 @@ function validateGenesisSteps(
2101
2155
  const mod = modules.find((m) => (m.domain ?? m.name) === s.domain);
2102
2156
  if (mod === undefined) {
2103
2157
  issues.push(
2104
- `${at} (${s.domain}/${s.directiveId}) targets domain '${s.domain}', which is NOT composed in this package — add its module to nomos.package.mjs \`domains\` (the one-package pattern: the parent must fold the child law so the kernel can resolve its bytes by hash; see bench/scale/fixtures/estate-birth)`,
2158
+ `${at} (${s.domain}/${s.directiveId}) targets domain '${s.domain}', which is NOT composed in this package — add its module to nomos.project.ts \`domains\` (the one-package pattern: the parent must fold the child law so the kernel can resolve its bytes by hash; see bench/scale/fixtures/estate-birth)`,
2105
2159
  );
2106
2160
  return;
2107
2161
  }
@@ -2381,9 +2435,9 @@ function discoverSingletonInstanceId(seedDir: Directive<unknown>, ownerMod?: Dom
2381
2435
  // ── the generator ─────────────────────────────────────────────────────────────────
2382
2436
 
2383
2437
  export interface TsProofOptions {
2384
- /** The package name (`nomos.package.mjs` `name`) — names the artifacts the proof loads. */
2438
+ /** The package name (`nomos.project.ts` `name`) — names the artifacts the proof loads. */
2385
2439
  readonly packageName: string;
2386
- /** sha256 of the built `.package.usda` — checked against the deploy response. */
2440
+ /** Content identity of the built typed USDA closure — checked against the install response. */
2387
2441
  readonly domainHash: string;
2388
2442
  /** Stable-id structural interface used to teach the offline harness the hidden owned-birth seal slots. */
2389
2443
  readonly interface?: Readonly<Record<string, DomainInterface>>;
@@ -2512,14 +2566,20 @@ function isStandaloneProofSafe(mod: DomainModule, directive: Directive<unknown>
2512
2566
  !(mod.workspaceInvariants ?? []).some((invariant) => invariant.on === directive!.id);
2513
2567
  }
2514
2568
 
2515
- /** Prefer the business create which demonstrates the richest typed relationship journey. Its prerequisites
2516
- * are generated automatically, so a join/create that consumes two NomosRefs is a better canonical example
2517
- * than an isolated leaf create. Stable source order breaks ties. */
2569
+ /** Prefer the strictest business create, then the one which demonstrates the richest typed relationship
2570
+ * journey. A capped aggregate is the stronger canonical proof: it exercises a workspace invariant and,
2571
+ * when the same action is also an authority seed, preserves isolation between standalone and bootstrap
2572
+ * legs. After that, a join/create consuming two NomosRefs is a better example than an isolated leaf create.
2573
+ * Stable source order breaks ties. */
2518
2574
  function orderedStandaloneCreates(mod: DomainModule): Directive<unknown>[] {
2575
+ const aggregates = new Map((mod.aggregates as AggregateHandle[]).map((aggregate) => [aggregate.id, aggregate]));
2519
2576
  return (mod.directives as Directive<unknown>[])
2520
2577
  .map((directive, index) => ({ directive, index }))
2521
2578
  .filter(({ directive }) => isStandaloneProofSafe(mod, directive))
2522
2579
  .sort((a, b) => {
2580
+ const capA = aggregates.get(a.directive.aggregateId ?? "")?.cap;
2581
+ const capB = aggregates.get(b.directive.aggregateId ?? "")?.cap;
2582
+ if ((capA !== undefined) !== (capB !== undefined)) return capA !== undefined ? -1 : 1;
2523
2583
  const refsA = Object.keys(aggregateReferenceFields(a.directive.payloadSchema as z.ZodTypeAny)).length;
2524
2584
  const refsB = Object.keys(aggregateReferenceFields(b.directive.payloadSchema as z.ZodTypeAny)).length;
2525
2585
  return refsB - refsA || a.index - b.index;
@@ -2532,6 +2592,7 @@ interface ProofReferenceSeed {
2532
2592
  readonly aggregate: AggregateHandle;
2533
2593
  readonly payload: Record<string, unknown>;
2534
2594
  readonly referenceFields: Readonly<Record<string, string>>;
2595
+ readonly optionalReferenceFields: readonly string[];
2535
2596
  readonly mintField?: string;
2536
2597
  }
2537
2598
 
@@ -2564,22 +2625,38 @@ function discoverProofReferenceSeeds(mod: DomainModule, target: Directive<unknow
2564
2625
  }
2565
2626
  const mintField = mintedCreateField(directive, aggregate);
2566
2627
  const referenceFields: Record<string, string> = { ...aggregateReferenceFields(directive.payloadSchema as z.ZodTypeAny) };
2628
+ const optionalReferenceFields = optionalAggregateReferenceFields(
2629
+ directive.payloadSchema as z.ZodTypeAny,
2630
+ );
2567
2631
  if (mintField !== undefined) delete referenceFields[mintField];
2568
- for (const prerequisite of Object.values(referenceFields)) visitType(prerequisite);
2632
+ const optionalReferenceSet = new Set(optionalReferenceFields);
2633
+ for (const [field, prerequisite] of Object.entries(referenceFields)) {
2634
+ if (!optionalReferenceSet.has(field)) visitType(prerequisite);
2635
+ }
2569
2636
  const synth = synthesizeDirectivePayload(directive, `${domain}/${directive.id}`);
2570
2637
  const payload: Record<string, unknown> = { ...synth.value };
2571
2638
  if (mintField !== undefined) delete payload[mintField];
2572
- out.push({ directive, aggregate, payload, referenceFields, ...(mintField !== undefined ? { mintField } : {}) });
2639
+ out.push({
2640
+ directive,
2641
+ aggregate,
2642
+ payload,
2643
+ referenceFields,
2644
+ optionalReferenceFields,
2645
+ ...(mintField !== undefined ? { mintField } : {}),
2646
+ });
2573
2647
  visiting.delete(aggregateId);
2574
2648
  emitted.add(aggregateId);
2575
2649
  };
2576
2650
 
2577
2651
  const targetReferences = { ...aggregateReferenceFields(target.payloadSchema as z.ZodTypeAny) };
2652
+ const targetOptionalReferences = new Set(
2653
+ optionalAggregateReferenceFields(target.payloadSchema as z.ZodTypeAny),
2654
+ );
2578
2655
  const targetAggregate = aggregates.find((aggregate) => aggregate.id === target.aggregateId);
2579
2656
  const targetMintField = targetAggregate === undefined ? undefined : mintedCreateField(target, targetAggregate);
2580
2657
  if (targetMintField !== undefined) delete targetReferences[targetMintField];
2581
- for (const aggregateId of Object.values(targetReferences)) {
2582
- visitType(aggregateId);
2658
+ for (const [field, aggregateId] of Object.entries(targetReferences)) {
2659
+ if (!targetOptionalReferences.has(field)) visitType(aggregateId);
2583
2660
  }
2584
2661
  return out;
2585
2662
  }
@@ -2728,6 +2805,11 @@ function attachBusinessEncryption(
2728
2805
  `${leg.parentDomain}/${leg.birthDirectiveId}: Nomos could not derive the hidden owned-birth custody map`,
2729
2806
  );
2730
2807
  }
2808
+ if (leg.childSeedInstanceId === undefined) {
2809
+ throw new Error(
2810
+ `${leg.parentDomain}/${leg.birthDirectiveId}: Nomos could not derive the owned birth root identity`,
2811
+ );
2812
+ }
2731
2813
  const fields: Record<string, { aggSid: string; fieldSid: string }> = {};
2732
2814
  for (const field of aggregate.encryptedFields ?? []) {
2733
2815
  const slot = Object.entries(offeredAggregate.fields).find(([, candidate]) => candidate.label === field);
@@ -2742,6 +2824,10 @@ function attachBusinessEncryption(
2742
2824
  ...leg,
2743
2825
  businessEncryption: {
2744
2826
  scope: aggregate.visibility.requires,
2827
+ recordType: isNamespacedAggregate(aggregate)
2828
+ ? qualifyReadProjectionType(leg.childDomain, aggregate.id)
2829
+ : aggregate.id,
2830
+ recordId: leg.childSeedInstanceId,
2745
2831
  namespace,
2746
2832
  keyField,
2747
2833
  fields,
@@ -3132,6 +3218,7 @@ function generateSealedApplicationProof(
3132
3218
  mod: DomainModule,
3133
3219
  opts: TsProofOptions,
3134
3220
  primaryLegs: ProofLegs,
3221
+ modules: readonly DomainModule[],
3135
3222
  ): string {
3136
3223
  const domain = mod.domain ?? mod.name;
3137
3224
  const applicationName = tsApplicationName(opts.packageName);
@@ -3141,9 +3228,22 @@ function generateSealedApplicationProof(
3141
3228
  const businessBirth = primaryLegs.childBirth?.businessAggregateId !== undefined
3142
3229
  ? primaryLegs.childBirth
3143
3230
  : undefined;
3144
- const businessBirthFollowup = businessBirth === undefined
3231
+ const selectedBirthDirective = businessBirth === undefined
3232
+ ? undefined
3233
+ : (mod.applicationIntents ?? mod.directives).find(
3234
+ (candidate) => candidate.id === businessBirth.birthDirectiveId,
3235
+ ) as Directive<unknown> | undefined;
3236
+ const applicationBirthMethod = businessBirth === undefined
3237
+ ? undefined
3238
+ : (selectedBirthDirective as { businessBirth?: unknown } | undefined)?.businessBirth !== undefined
3239
+ ? isBusinessIntent(selectedBirthDirective!) ? selectedBirthDirective!.id : "birth"
3240
+ : businessBirth.birthDirectiveId;
3241
+ const bornModule = businessBirth === undefined
3145
3242
  ? undefined
3146
- : (mod.applicationIntents ?? mod.directives).flatMap((candidate) => {
3243
+ : modules.find((candidate) => (candidate.domain ?? candidate.name) === businessBirth.childDomain);
3244
+ const businessBirthFollowup = businessBirth === undefined || bornModule === undefined
3245
+ ? undefined
3246
+ : orderedStandaloneCreates(bornModule).flatMap((candidate) => {
3147
3247
  const directive = candidate as Directive<unknown>;
3148
3248
  if (
3149
3249
  (directive as { businessBirthInternal?: unknown }).businessBirthInternal === true ||
@@ -3153,7 +3253,9 @@ function generateSealedApplicationProof(
3153
3253
  typeof directive.aggregateId !== "string" ||
3154
3254
  Object.keys(aggregateReferenceFields(directive.payloadSchema as z.ZodTypeAny)).length > 0
3155
3255
  ) return [];
3156
- const target = (mod.aggregates as AggregateHandle[]).find((aggregate) => aggregate.id === directive.aggregateId);
3256
+ const target = (bornModule.aggregates as AggregateHandle[]).find(
3257
+ (aggregate) => aggregate.id === directive.aggregateId,
3258
+ );
3157
3259
  if (target === undefined) return [];
3158
3260
  try {
3159
3261
  const synth = synthesizeDirectivePayload(directive, `${domain}/${directive.id}`);
@@ -3179,9 +3281,14 @@ function generateSealedApplicationProof(
3179
3281
  ...referenceSeeds.flatMap((seed) => Object.values(seed.referenceFields)),
3180
3282
  ...(action === undefined ? [] : Object.values(aggregateReferenceFields(action.payloadSchema as z.ZodTypeAny))),
3181
3283
  ])].filter((type) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(type)).sort();
3284
+ const hasOptionalReferenceChoices = [
3285
+ ...referenceSeeds.flatMap((seed) => seed.optionalReferenceFields),
3286
+ ...(action === undefined ? [] : optionalAggregateReferenceFields(action.payloadSchema as z.ZodTypeAny)),
3287
+ ].length > 0;
3182
3288
  const typeImports = referenceTypes.length > 0
3183
3289
  ? `, type NomosRef, ${referenceTypes.map((type) => `type ${type}`).join(", ")}`
3184
3290
  : "";
3291
+ const optionalReferenceImport = hasOptionalReferenceChoices ? ", NomosOptionalRef" : "";
3185
3292
  const refDeclarations = referenceTypes.flatMap((type) => [
3186
3293
  `let ${proofRefSlot(type)}: NomosRef<${type}> | undefined;`,
3187
3294
  `const ${proofRefGetter(type)} = (): NomosRef<${type}> => ${proofRefSlot(type)} ?? fail(${JSON.stringify(`generated proof ordering error: NomosRef<${type}> is not available yet`)});`,
@@ -3191,20 +3298,24 @@ function generateSealedApplicationProof(
3191
3298
  `// Regenerated from the business law; do not edit or maintain a second client example.`,
3192
3299
  `// The local proof runner executes every deep framework leg without exposing it here.`,
3193
3300
  `// @nomos-proof-legs ${marker}`,
3194
- `import { ${opener}, sameRecord${typeImports} } from "./${opts.packageName}.client.mjs";`,
3301
+ `import { ${opener}, sameRecord${optionalReferenceImport}${typeImports} } from "./${opts.packageName}.client.mjs";`,
3195
3302
  ``,
3196
3303
  `const fail: (message: string) => never = (message) => { throw new Error(message); };`,
3197
3304
  `const ok = (message: string) => console.log("✓ " + message);`,
3198
3305
  `if (process.env.NOMOS_PROOF_MANAGED !== "1") fail("run the live journey through: githolon proof --live");`,
3199
- `const cloud = (process.env.NOMOS_CLOUD || "https://nomos.captainapp.co.uk").replace(/\\/+$/, "");`,
3306
+ `const cloud = (process.env.NOMOS_CLOUD || "https://nomos.cafe").replace(/\\/+$/, "");`,
3200
3307
  `const applicationName = process.env.NOMOS_APPLICATION || ${JSON.stringify(opts.packageName)};`,
3308
+ `const proofUid = process.env.NOMOS_PROOF_SUBJECT || "";`,
3309
+ `const proofPrincipal = proofUid ? "user:" + proofUid : "";`,
3201
3310
  `const signIn = async (request: { readonly deviceChallenge?: string } = {}) => {`,
3202
- ` if (!request.deviceChallenge) return process.env.NOMOS_AUTH_TOKEN || "";`,
3203
- ` const subject = process.env.NOMOS_PROOF_SUBJECT || "";`,
3204
- ` if (!subject) fail("the generated blank-user proof has no authority subject");`,
3205
- ` const response = await fetch(cloud + "/dev/mint-token", {`,
3206
- ` method: "POST", headers: { "content-type": "application/json" },`,
3207
- ` body: JSON.stringify({ sub: subject, nonce: request.deviceChallenge }),`,
3311
+ ` const session = process.env.NOMOS_AUTH_TOKEN || "";`,
3312
+ ` if (!request.deviceChallenge) return session;`,
3313
+ ` if (!proofUid) fail("the generated blank-user proof has no authority subject");`,
3314
+ ` const endpoint = process.env.NOMOS_DEVICE_TOKEN_ENDPOINT || "https://id.nomos.captainapp.co.uk/token";`,
3315
+ ` const localTestIdp = endpoint.endsWith("/dev/mint-token");`,
3316
+ ` const response = await fetch(endpoint, {`,
3317
+ ` method: "POST", headers: { "content-type": "application/json", ...(session ? { authorization: "Bearer " + session } : {}) },`,
3318
+ ` body: JSON.stringify(localTestIdp ? { sub: proofUid, nonce: request.deviceChallenge } : { nonce: request.deviceChallenge }),`,
3208
3319
  ` });`,
3209
3320
  ` const body = await response.json() as { token?: string; error?: string };`,
3210
3321
  ` if (!response.ok || !body.token) fail("the generated application could not attest its private device: " + (body.error || response.status));`,
@@ -3234,7 +3345,7 @@ function generateSealedApplicationProof(
3234
3345
  );
3235
3346
  out.push(
3236
3347
  `const facts = ${JSON.stringify(businessPayload)};`,
3237
- `const born = await application.birth(facts);`,
3348
+ `const born = await application.${applicationBirthMethod!}(facts);`,
3238
3349
  `if (born === undefined) fail("business birth returned no child application");`,
3239
3350
  `await waitUntilSaved();`,
3240
3351
  `ok(${JSON.stringify(`birth(${businessBirth.businessAggregateId}) saved through the generated application`)});`,
@@ -3253,7 +3364,12 @@ function generateSealedApplicationProof(
3253
3364
  }
3254
3365
  } else if (action !== undefined && aggregate !== undefined) {
3255
3366
  for (const seed of referenceSeeds) {
3256
- const payload = typedPayloadLiteral(seed.payload, seed.referenceFields);
3367
+ const payload = typedPayloadLiteral(
3368
+ seed.payload,
3369
+ seed.referenceFields,
3370
+ {},
3371
+ new Set(seed.optionalReferenceFields),
3372
+ );
3257
3373
  const variable = `seed${proofTypeIdent(seed.aggregate.id)}`;
3258
3374
  out.push(
3259
3375
  `const ${variable}Result = await application.${seed.directive.id}(${payload});`,
@@ -3266,11 +3382,25 @@ function generateSealedApplicationProof(
3266
3382
  const mintField = mintedCreateField(action, aggregate);
3267
3383
  const payload = { ...synth.value };
3268
3384
  if (mintField !== undefined) delete payload[mintField];
3269
- const payloadLiteral = typedPayloadLiteral(payload, refs);
3385
+ const optionalRefs = new Set(optionalAggregateReferenceFields(action.payloadSchema as z.ZodTypeAny));
3386
+ const bornContext = primaryLegs.relationBootstrap?.bornContext;
3387
+ const authorityPlaceholder = bornContext?.ownerField === undefined
3388
+ ? undefined
3389
+ : bornContext.birthPayload[bornContext.ownerField];
3390
+ const authorityOverrides = typeof authorityPlaceholder !== "string"
3391
+ ? {}
3392
+ : Object.fromEntries(
3393
+ Object.entries(payload)
3394
+ .filter(([, value]) => value === authorityPlaceholder)
3395
+ .map(([field]) => [field, "proofPrincipal"]),
3396
+ );
3397
+ const payloadLiteral = typedPayloadLiteral(payload, refs, authorityOverrides, optionalRefs);
3270
3398
  const nullArmLines = nullablePayloadFields(action.payloadSchema as z.ZodTypeAny).flatMap((field) => {
3271
3399
  const nullPayload = { ...payload, [field]: null };
3400
+ const nullOverrides = { ...authorityOverrides };
3401
+ delete nullOverrides[field];
3272
3402
  return [
3273
- `await application.${action.id}(${typedPayloadLiteral(nullPayload, refs)});`,
3403
+ `await application.${action.id}(${typedPayloadLiteral(nullPayload, refs, nullOverrides, optionalRefs)});`,
3274
3404
  `ok(${JSON.stringify(`NULL-ARM PROOF — ${domain}/${action.id} accepted ${field}=null`)});`,
3275
3405
  ];
3276
3406
  });
@@ -3305,7 +3435,7 @@ function generateSealedApplicationProof(
3305
3435
  ...clearReadLines,
3306
3436
  `const reopened = await ${opener}({ cloud, application: applicationName, signIn });`,
3307
3437
  `const rows = await reopened.${generatedAggregateCollectionName(aggregate.id)}.all();`,
3308
- `if (!rows.some((row) => sameRecord(row.ref, created))) fail("a fresh generated application could not read the saved record");`,
3438
+ `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)"));`,
3309
3439
  `ok("a fresh generated application read the saved record after synchronization");`,
3310
3440
  );
3311
3441
  } else {
@@ -3365,5 +3495,5 @@ export function generateTsProof(modules: readonly DomainModule[], opts: TsProofO
3365
3495
  // marker, and the index can never describe different directives. The primary domain's legs carry whichever
3366
3496
  // of standalone / child-birth / relation-bootstrap it has.
3367
3497
  const primaryLegs = legsForDomain(mod, modules, opts.interface);
3368
- return generateSealedApplicationProof(mod, opts, primaryLegs);
3498
+ return generateSealedApplicationProof(mod, opts, primaryLegs, modules);
3369
3499
  }
@@ -67,6 +67,12 @@ import {
67
67
  * fragile structural inference over the flattened directive list.
68
68
  */
69
69
  export interface ImpureCapabilityDecl {
70
+ /** Law-facing binding name used by the outbox/provider registry. */
71
+ readonly name?: string;
72
+ /** HTTPS offer endpoint; absent means obligations remain locally requested. */
73
+ readonly endpoint?: string;
74
+ /** Maintained requested-task read used by the executor scan lane. */
75
+ readonly tasks?: { readonly id: string };
70
76
  /** The task aggregate handle; `.id` is the wire type (e.g. `TranscribeVoiceNoteAggregate`). */
71
77
  readonly aggregate: { readonly id: string };
72
78
  /** The Obligation directive: `.creates`, payload = envelope ⊕ tenant params. */
@@ -184,7 +190,9 @@ function obligationFromJsonExpr(s: PayloadFieldSpec): string {
184
190
  }
185
191
  case "wrapped": {
186
192
  // typed-id class (String wire) or unit extension type (num wire): wrap the raw scalar.
187
- const wrap = (v: string) => `${s.dartType}${s.dartType.endsWith("Id") ? `.fromRow(${v} as String)` : `((${v} as num).toDouble())`}`;
193
+ const wrap = (v: string) => s.wrappedScalarKind === "number"
194
+ ? `${s.dartType}((${v} as num).toDouble())`
195
+ : `${s.dartType}(${v} as String)`;
188
196
  return s.optional ? `${raw} == null ? null : ${wrap(raw)}` : wrap(raw);
189
197
  }
190
198
  case "ref": {
@@ -194,6 +202,14 @@ function obligationFromJsonExpr(s: PayloadFieldSpec): string {
194
202
  `NmsInternalNomosRefs.create<${target}>(workspace: '', type: '${wireType}', id: ${v} as String)`;
195
203
  return s.optional ? `${raw} == null ? null : ${wrap(raw)}` : wrap(raw);
196
204
  }
205
+ case "versionedRef": {
206
+ const target = s.versionedRefTarget as string;
207
+ const series = s.versionedRefSeries as string;
208
+ const currentField = JSON.stringify(s.versionedRefCurrentField);
209
+ const wrap = (v: string) =>
210
+ `() { final m = (${v} as Map).cast<String, Object?>(); return NmsInternalVersionedNomosRefs.create<${target}, ${series}>(pinned: NmsInternalNomosRefs.fromJson<${target}>(m['pinned']), series: NmsInternalNomosRefs.fromJson<${series}>(m['series']), currentField: ${currentField}); }()`;
211
+ return s.optional ? `${raw} == null ? null : ${wrap(raw)}` : wrap(raw);
212
+ }
197
213
  case "recipient": {
198
214
  const wrap = (v: string) => `NmsInternalInvitations.recipient<User>(${v} as String)`;
199
215
  return s.optional ? `${raw} == null ? null : ${wrap(raw)}` : wrap(raw);