@githolon/dsl 0.103.4 → 0.103.6

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 (40) hide show
  1. package/dart/CHANGELOG.md +8 -0
  2. package/dart/lib/src/runner/node-entry.mjs +333 -117
  3. package/dart/lib/src/runner/offline-install.mjs +173 -67
  4. package/dart/lib/src/session_bound.dart +111 -2
  5. package/dart/lib/src/test_harness.dart +51 -41
  6. package/dart/lib/src/webview_bridge.dart +714 -60
  7. package/dart/pubspec.lock +1 -1
  8. package/dart/pubspec.yaml +2 -2
  9. package/dart/test/acquire_session_test.dart +62 -5
  10. package/dart/test/cloud_bound_topology_test.dart +59 -38
  11. package/dart/test/session_bound_test.dart +25 -10
  12. package/dart/test/webview_bridge_test.dart +326 -7
  13. package/dart_types/CHANGELOG.md +5 -0
  14. package/dart_types/lib/nomos_types.dart +1 -0
  15. package/dart_types/lib/src/git_object_id.dart +37 -0
  16. package/dart_types/pubspec.yaml +1 -1
  17. package/package.json +1 -1
  18. package/runtime/generated-application-host.js +347 -113
  19. package/src/births.ts +16 -1
  20. package/src/business_birth.ts +55 -34
  21. package/src/codegen_dart.ts +68 -12
  22. package/src/codegen_dart_application.ts +114 -19
  23. package/src/codegen_dart_client.ts +149 -6
  24. package/src/codegen_proof.ts +16 -9
  25. package/src/codegen_provider_dart.ts +5 -0
  26. package/src/codegen_ts.ts +115 -19
  27. package/src/compile_package_main.ts +159 -43
  28. package/src/fields.ts +43 -0
  29. package/src/framework/domain_lifecycle.ts +108 -88
  30. package/src/framework/genesis.ts +2 -1
  31. package/src/framework/home.ts +6 -43
  32. package/src/framework/workspaces.ts +42 -25
  33. package/src/idof_lint.ts +65 -51
  34. package/src/index.ts +21 -1
  35. package/src/intent.ts +11 -1
  36. package/src/law_graph.ts +81 -11
  37. package/src/relation_schema.ts +18 -0
  38. package/src/shape_compat.ts +3 -0
  39. package/src/stable_ids_for_module.ts +7 -1
  40. package/src/value_object.ts +40 -0
@@ -7,7 +7,7 @@ import type { z } from "zod";
7
7
 
8
8
  import type { AggregateHandle } from "./aggregate.js";
9
9
  import type { Directive } from "./directive.js";
10
- import { isSha256DigestSchema, type Field } from "./fields.js";
10
+ import { isGitObjectIdSchema, isSha256DigestSchema, type Field } from "./fields.js";
11
11
  import type { QueryDecl } from "./query.js";
12
12
  import type { DomainModule } from "./codegen_dart.js";
13
13
  import {
@@ -89,6 +89,7 @@ const TENANT_ROOT_MEMBERS = new Map<string, string>([
89
89
  ["currentUser", "the generated signed-in application participant"],
90
90
  ["currentStatus", "the generated application status"],
91
91
  ["watchStatus", "the generated application status watch"],
92
+ ["whenCurrent", "the generated application convergence awaitable"],
92
93
  ["recipient", "the generated typed recipient boundary"],
93
94
  ["createInvitation", "the generated invitation capability"],
94
95
  ["redeemInvitation", "the generated invitation redemption"],
@@ -129,6 +130,12 @@ function keyType(aggregate: AggregateHandle | undefined, key: string): string {
129
130
  ) {
130
131
  return "Sha256Digest";
131
132
  }
133
+ if (
134
+ field?.kind === "string" &&
135
+ isGitObjectIdSchema(field.zod as z.ZodTypeAny)
136
+ ) {
137
+ return "GitObjectId";
138
+ }
132
139
  return "String";
133
140
  }
134
141
 
@@ -537,6 +544,17 @@ export function generateDartApplication(
537
544
  if (uri.endsWith(`/${name}.dart`) || uri.endsWith(`${name}.dart`))
538
545
  return uri;
539
546
  }
547
+ // In layered output the domain types live in the generated TYPES package,
548
+ // never beside this application library. A relative fallback here makes
549
+ // the client package unmountable as soon as a generated application needs
550
+ // a concrete sibling-domain type (for example a typed NomosRef carried by
551
+ // a parent access card). Derive the same public package URI used by the
552
+ // compiler's explicit-barrel imports; flat output deliberately keeps the
553
+ // sibling-relative form below.
554
+ const layeredTypes = opts.typesImportUri?.match(/^package:([^/]+)\//);
555
+ if (layeredTypes?.[1] !== undefined) {
556
+ return `package:${layeredTypes[1]}/src/generated/${name}.dart`;
557
+ }
540
558
  return `${name}.dart`;
541
559
  };
542
560
 
@@ -573,7 +591,11 @@ export function generateDartApplication(
573
591
  )
574
592
  );
575
593
  return specs
576
- .filter((spec) => spec.toJson === "ref" && !recordInputIds.has(spec.name))
594
+ .filter(
595
+ (spec) =>
596
+ (spec.toJson === "ref" || spec.toJson === "addressRef") &&
597
+ !recordInputIds.has(spec.name)
598
+ )
577
599
  .map((spec) => {
578
600
  const targetClass =
579
601
  spec.dartType.match(/^NomosRef<(.*)>$/)?.[1] ?? "Object?";
@@ -583,7 +605,8 @@ export function generateDartApplication(
583
605
  (aggregate) => concreteClassName(aggregate.id) === targetClass
584
606
  )?.id ??
585
607
  targetClass;
586
- const expectedWorkspace = foreignReferenceIds.has(spec.name)
608
+ const expectedWorkspace =
609
+ spec.toJson === "addressRef" || foreignReferenceIds.has(spec.name)
587
610
  ? "''"
588
611
  : "_client.session?.workspace ?? ''";
589
612
  const value = `facts.${member(spec.name)}`;
@@ -800,6 +823,31 @@ export function generateDartApplication(
800
823
  path
801
824
  .split(".")
802
825
  .reduce((expr, part) => `${expr}.${member(part)}`, "facts");
826
+ const foreignInputs = dartForeignReferenceInputs(
827
+ directive,
828
+ mod.aggregates
829
+ );
830
+ // A generated input collapses a foreign `(workspace, id)` pair into one
831
+ // NomosRef. Source-trust orchestration must follow that public shape too:
832
+ // deriving the locator from the typed reference, rather than reaching for
833
+ // the deliberately removed raw workspace/id fields.
834
+ const factValue = (path: string): string => {
835
+ if (!path.includes(".")) {
836
+ const workspaceOwner = foreignInputs.find(
837
+ (input) => input.workspaceField === path
838
+ );
839
+ if (workspaceOwner !== undefined) {
840
+ return `NmsInternalNomosRefs.workspace(facts.${member(
841
+ workspaceOwner.idField
842
+ )})`;
843
+ }
844
+ const reference = foreignInputs.find((input) => input.idField === path);
845
+ if (reference !== undefined) {
846
+ return `NmsInternalNomosRefs.id(facts.${member(reference.idField)})`;
847
+ }
848
+ }
849
+ return factPath(path);
850
+ };
803
851
  const trust = directive.businessSourceTrust;
804
852
  const method = member(directive.id);
805
853
  if (trust === undefined) {
@@ -809,11 +857,11 @@ export function generateDartApplication(
809
857
  .sort(([a], [b]) => a.localeCompare(b))
810
858
  .map(
811
859
  ([target, field]) =>
812
- ` ${JSON.stringify(target)}: ${factPath(field)},`
860
+ ` ${JSON.stringify(target)}: ${factValue(field)},`
813
861
  );
814
862
  return {
815
863
  prelude: [
816
- ` final sourceGenesis = await _client.bridge.nomosWorkspaceGenesis(${factPath(
864
+ ` final sourceGenesis = await _client.bridge.nomosWorkspaceGenesis(${factValue(
817
865
  trust.fromPayload
818
866
  )});`,
819
867
  ` await _client._offer(`,
@@ -1108,6 +1156,13 @@ export function generateDartApplication(
1108
1156
  ` return opened;`,
1109
1157
  ` }`,
1110
1158
  ``,
1159
+ ` /// Open the workspace named by a typed ${rootType} custody reference.`,
1160
+ ` /// The reference supplies identity and routing only; normal authority checks still apply.`,
1161
+ ` Future<${targetApplication}> openReference(NomosRef<${rootType}> reference) {`,
1162
+ ` _nmsRequireReference(reference, ${JSON.stringify(birth.aggregateId)}, '');`,
1163
+ ` return _nmsOpenReference(reference);`,
1164
+ ` }`,
1165
+ ``,
1111
1166
  ` /// Open the ${birth.aggregateId} whose ${birth.keyField} is [key].`,
1112
1167
  ` Future<${targetApplication}> open(String key) => _nmsOpenReference(`,
1113
1168
  ` NmsInternalNomosRefs.create<${rootType}>(`,
@@ -1277,6 +1332,7 @@ export function generateDartApplication(
1277
1332
  actionResultDeclarations.push(
1278
1333
  `/// Every business record affected by ${cap(camel(directive.id))}.`,
1279
1334
  `final class ${resultClass} {`,
1335
+ ` final NomosOperationReceipt receipt;`,
1280
1336
  ...outputs.map((output) =>
1281
1337
  output.many
1282
1338
  ? ` final List<NomosRef<${output.dartType}>> ${output.field};`
@@ -1284,7 +1340,7 @@ export function generateDartApplication(
1284
1340
  ? ` final NomosRef<${output.dartType}>? ${output.field};`
1285
1341
  : ` final NomosRef<${output.dartType}> ${output.field};`
1286
1342
  ),
1287
- ` const ${resultClass}({${outputs
1343
+ ` const ${resultClass}({required this.receipt, ${outputs
1288
1344
  .map((output) => `required this.${output.field}`)
1289
1345
  .join(", ")}});`,
1290
1346
  `}`,
@@ -1294,7 +1350,7 @@ export function generateDartApplication(
1294
1350
  const resultExpression =
1295
1351
  outputs.length === 0
1296
1352
  ? undefined
1297
- : `${resultClass}(${outputs
1353
+ : `${resultClass}(receipt: change.receipt, ${outputs
1298
1354
  .map((output) =>
1299
1355
  output.many
1300
1356
  ? `${output.field}: _nmsAffectedReferences<${
@@ -1331,11 +1387,12 @@ export function generateDartApplication(
1331
1387
  ` /// ${cap(camel(directive.id))}. ${businessActionDoc(directive)}`,
1332
1388
  ` /// Canonical write path: pass business facts here; never mirror this action in application state`,
1333
1389
  ` /// or call a lower-level client.`,
1334
- ` Future<void> ${method}(${payload} facts) async {`,
1390
+ ` Future<NomosOperationReceipt> ${method}(${payload} facts) async {`,
1335
1391
  ...referenceChecks,
1336
1392
  ...sourceTrust.prelude,
1337
- ` await ${sourceTrust.call};`,
1393
+ ` final change = await ${sourceTrust.call};`,
1338
1394
  ...afterBusinessWrite(directive),
1395
+ ` return change.receipt;`,
1339
1396
  ` }`,
1340
1397
  ``
1341
1398
  );
@@ -1359,10 +1416,11 @@ export function generateDartApplication(
1359
1416
  } else {
1360
1417
  addCollectionMember(directive.aggregateId, method, [
1361
1418
  ` /// ${cap(camel(directive.id))}.`,
1362
- ` Future<void> ${method}(${payload} facts) async {`,
1419
+ ` Future<NomosOperationReceipt> ${method}(${payload} facts) async {`,
1363
1420
  ...referenceChecks,
1364
1421
  ...sourceTrust.prelude,
1365
- ` await ${sourceTrust.call};`,
1422
+ ` final change = await ${sourceTrust.call};`,
1423
+ ` return change.receipt;`,
1366
1424
  ` }`,
1367
1425
  ``,
1368
1426
  ]);
@@ -1834,10 +1892,16 @@ export function generateDartApplication(
1834
1892
  ``,
1835
1893
  ]);
1836
1894
  addCollectionMember(aggregate.id, "all", [
1895
+ ` @Deprecated(${JSON.stringify(
1896
+ `Reads every ${concrete} in the workspace. Prefer find(reference) or a declared keyed query; use all() only for an intentional whole-workspace operation.`
1897
+ )})`,
1837
1898
  ` Future<List<${model}>> all() => _client.${clientList}();`,
1838
1899
  ``,
1839
1900
  ]);
1840
1901
  addCollectionMember(aggregate.id, "watchAll", [
1902
+ ` @Deprecated(${JSON.stringify(
1903
+ `Watches every ${concrete} in the workspace. Prefer watchOne(reference) or a declared keyed watch; use watchAll() only for an intentional whole-workspace operation.`
1904
+ )})`,
1841
1905
  ` Stream<List<${model}>> watchAll() => _client.watchAll${Cap}s();`,
1842
1906
  ``,
1843
1907
  ]);
@@ -1901,7 +1965,7 @@ export function generateDartApplication(
1901
1965
  ...(sharingPolicies.length > 0
1902
1966
  ? [`import 'dart:async' show StreamSubscription, unawaited;`]
1903
1967
  : []),
1904
- `import '${runtimeImport}' show NmsInternalForeignRefs, NmsInternalNomosRefs, NmsInternalRecordOrigins, NomosBridge, NomosOfferOutcome, NomosRef, NomosSession${
1968
+ `import '${runtimeImport}' show NmsInternalForeignRefs, NmsInternalNomosRefs, NmsInternalRecordOrigins, NomosBridge, NomosOfferOutcome, NomosOperationReceipt, NomosRef, NomosSession${
1905
1969
  usesWorkspaceName ? ", deriveWorkspaceName" : ""
1906
1970
  }${
1907
1971
  sharingPolicies.length > 0
@@ -1918,15 +1982,17 @@ export function generateDartApplication(
1918
1982
  `import 'package:nomos_flutter/src/application_share.dart' show ApplicationShareResult;`,
1919
1983
  ]
1920
1984
  : []),
1921
- ...(opts.flutterHost === true && tenantRoot
1985
+ ...(opts.flutterHost === true
1922
1986
  ? [
1923
- `import 'package:nomos_flutter/src/application_status.dart' show ApplicationSignIn, ApplicationStatus;`,
1987
+ `import 'package:nomos_flutter/src/application_status.dart' show ${
1988
+ tenantRoot ? "ApplicationSignIn, " : ""
1989
+ }ApplicationStatus;`,
1924
1990
  ]
1925
1991
  : []),
1926
1992
  `import 'package:nomos_flutter/src/generated_application_host.dart' show ${
1927
- tenantRoot ? "NomosApplicationStartupState, " : ""
1993
+ tenantRoot ? "ApplicationModelChange, NomosApplicationStartupState, " : ""
1928
1994
  }NmsInternalApplicationUserBinding${
1929
- tenantRoot || sharingPolicies.length > 0
1995
+ opts.flutterHost === true || sharingPolicies.length > 0
1930
1996
  ? ", NmsInternalApplicationHost"
1931
1997
  : ""
1932
1998
  }${
@@ -1978,14 +2044,25 @@ export function generateDartApplication(
1978
2044
  ``,
1979
2045
  ` /// Prove, adopt or restore the law this application was generated against.`,
1980
2046
  ` NomosModel get model => _client.model;`,
1981
- ...(opts.flutterHost === true && tenantRoot
2047
+ ...(opts.flutterHost === true
1982
2048
  ? [
1983
2049
  ``,
1984
- ` /// Current durability and convergence in product language.`,
2050
+ ` /// Current durability and convergence for this workspace in product language.`,
1985
2051
  ` ApplicationStatus get currentStatus => NmsInternalApplicationHost.currentStatus(_client.bridge);`,
1986
2052
  ``,
1987
- ` /// React whenever this application saves, receives work, goes offline or needs attention.`,
2053
+ ` /// React whenever this workspace saves, receives work, goes offline or needs attention.`,
1988
2054
  ` Stream<ApplicationStatus> watchStatus() => NmsInternalApplicationHost.watchStatus(_client.bridge);`,
2055
+ ``,
2056
+ ` /// Wait until accepted local work is in shared custody and this workspace has caught up.`,
2057
+ ` Future<void> whenCurrent({Duration timeout = const Duration(minutes: 2)}) =>`,
2058
+ ` NmsInternalApplicationHost.whenCurrent(_client.bridge, timeout: timeout);`,
2059
+ ``,
2060
+ ` /// The parent operation which created this workspace during this application run, if any.`,
2061
+ ` NomosOperationReceipt? get creationReceipt => NmsInternalApplicationHost.creationReceipt(_client.bridge);`,
2062
+ ]
2063
+ : []),
2064
+ ...(opts.flutterHost === true && tenantRoot
2065
+ ? [
1989
2066
  ``,
1990
2067
  ` /// Mount the one application at the UI boundary and pass [application] down.`,
1991
2068
  ` ///`,
@@ -2001,12 +2078,16 @@ export function generateDartApplication(
2001
2078
  ` required Widget Function(BuildContext context, ${application} application) builder,`,
2002
2079
  ` Widget Function(BuildContext context)? loadingBuilder,`,
2003
2080
  ` Widget Function(BuildContext context, Object error, VoidCallback retry)? errorBuilder,`,
2081
+ ` ApplicationModelChange? onModelChange,`,
2004
2082
  ` ValueChanged<NomosApplicationStartupState>? onStartupStateChanged,`,
2083
+ ` Duration startupTimeout = const Duration(seconds: 45),`,
2005
2084
  ` }) => NmsInternalApplicationHost.home<${application}>(`,
2006
2085
  ` key: key, cloud: cloud, application: application, signIn: signIn,`,
2007
2086
  ` bind: nomosInternalBind${application}, builder: builder,`,
2008
2087
  ` loadingBuilder: loadingBuilder, errorBuilder: errorBuilder,`,
2088
+ ` onModelChange: onModelChange,`,
2009
2089
  ` onStartupStateChanged: onStartupStateChanged,`,
2090
+ ` startupTimeout: startupTimeout,`,
2010
2091
  ` );`,
2011
2092
  ]
2012
2093
  : []),
@@ -2132,6 +2213,13 @@ export function generateDartApplication(
2132
2213
  ``,
2133
2214
  `Future<${application}> nomosInternalOpen${application}<TAggregate>(`,
2134
2215
  ` NomosBridge bridge, NomosRef<TAggregate> reference, String? actor) async {`,
2216
+ ...(opts.flutterHost === true
2217
+ ? [
2218
+ ` return NmsInternalApplicationHost.nmsInternalOpenReferencedApplication<${application}>(`,
2219
+ ` parent: bridge,`,
2220
+ ` open: () async {`,
2221
+ ]
2222
+ : []),
2135
2223
  ...(tenantRoot
2136
2224
  ? [
2137
2225
  ` final signedIn = actor?.trim() ?? '';`,
@@ -2151,6 +2239,13 @@ export function generateDartApplication(
2151
2239
  ? [` await application._nmsStartWorkspaceSharing();`]
2152
2240
  : []),
2153
2241
  ` return application;`,
2242
+ ...(opts.flutterHost === true
2243
+ ? [
2244
+ ` },`,
2245
+ ` bridgeOf: (application) => application._client.bridge,`,
2246
+ ` );`,
2247
+ ]
2248
+ : []),
2154
2249
  `}`,
2155
2250
  ``,
2156
2251
  ].join("\n");
@@ -35,7 +35,7 @@ import {
35
35
  OWNED_PREMINTED_RECORDS_PAYLOAD_FIELD,
36
36
  type AggregateHandle,
37
37
  } from "./aggregate.js";
38
- import { isSha256DigestSchema, type Field } from "./fields.js";
38
+ import { isGitObjectIdSchema, isSha256DigestSchema, type Field } from "./fields.js";
39
39
  import type { Directive } from "./directive.js";
40
40
  import type { QueryDecl } from "./query.js";
41
41
  import type { DomainModule } from "./codegen_dart.js";
@@ -165,6 +165,9 @@ function dartKeyType(agg: AggregateHandle | undefined, key: string): string {
165
165
  if (f?.kind === "string" && isSha256DigestSchema(f.zod as z.ZodTypeAny)) {
166
166
  return "Sha256Digest";
167
167
  }
168
+ if (f?.kind === "string" && isGitObjectIdSchema(f.zod as z.ZodTypeAny)) {
169
+ return "GitObjectId";
170
+ }
168
171
  return "String";
169
172
  }
170
173
 
@@ -178,6 +181,12 @@ function dartKeyExpr(agg: AggregateHandle | undefined, key: string): string {
178
181
  ) {
179
182
  return `${param}.value`;
180
183
  }
184
+ if (
185
+ field?.kind === "string" &&
186
+ isGitObjectIdSchema(field.zod as z.ZodTypeAny)
187
+ ) {
188
+ return `${param}.value`;
189
+ }
181
190
  if (field?.kind !== "ref") return param;
182
191
  return field.foreignRef !== undefined
183
192
  ? `NmsInternalNomosRefs.id(${param}.ref)`
@@ -210,6 +219,10 @@ function rowToModel(model: string, rowVar: string, workspace = "_ws"): string {
210
219
  export interface DartBridgeClientOptions {
211
220
  /** Content identity of the built typed USDA closure (`.law.usdz`). */
212
221
  readonly domainHash: string;
222
+ /** Flutter asset key for the compiler-authored USDZ transport. Generated
223
+ * child openers use it to inherit an accepted parent model through the
224
+ * child's ordinary author gate. Omit for pure-Dart/vendored clients. */
225
+ readonly lawAssetKey?: string;
213
226
  /** This domain's STRUCTURAL INTERFACE (#72) — directives (payload shapes) + aggregates
214
227
  * (read shapes, sid-keyed). Baked in as JSON so the app can negotiate shape-compatibility
215
228
  * against the workspace's installed offer, NOT by law hash. */
@@ -1602,6 +1615,19 @@ export function generateDartBridgeClient(
1602
1615
  ` differences.add(NomosModelDifference(kind: kind, area: area, action: action, summary: summary));`,
1603
1616
  ` }`,
1604
1617
  ` final evolve = (report?['evolve'] as Map?)?.cast<String, Object?>();`,
1618
+ ` for (final raw in ((evolve?['violations'] as List?) ?? const [])) {`,
1619
+ ` if (raw is! Map) continue;`,
1620
+ ` final violation = raw.cast<String, Object?>();`,
1621
+ ` final rawDomain = violation['domain']?.toString().trim();`,
1622
+ ` final rawDetail = violation['detail']?.toString().trim();`,
1623
+ ` differences.add(NomosModelDifference(`,
1624
+ ` kind: NomosModelDifferenceKind.rules,`,
1625
+ ` area: rawDomain == null || rawDomain.isEmpty ? null : rawDomain,`,
1626
+ ` summary: rawDetail == null || rawDetail.isEmpty`,
1627
+ ` ? 'This model does not declare how to interpret an existing value.'`,
1628
+ ` : rawDetail,`,
1629
+ ` ));`,
1630
+ ` }`,
1605
1631
  ` if (report != null && evolve?['ok'] != true && !differences.any((d) => d.kind == NomosModelDifferenceKind.rules)) {`,
1606
1632
  ` differences.add(const NomosModelDifference(kind: NomosModelDifferenceKind.rules, summary: 'This model does not yet explain how to interpret every existing value.'));`,
1607
1633
  ` }`,
@@ -1629,13 +1655,30 @@ export function generateDartBridgeClient(
1629
1655
  ` return NomosModelAdoption.adopted(`,
1630
1656
  ` summary: 'Nomos proved this model against current history and made it active.', review: review);`,
1631
1657
  ` }`,
1658
+ ...(opts.lawAssetKey === undefined
1659
+ ? []
1660
+ : [
1661
+ ` if (stage == 'staging-required') {`,
1662
+ ` return NomosModelAdoption.needsStaging(`,
1663
+ ` summary: 'This workspace is running an older model. Stage this generated USDA candidate through the signed workspace gate before reviewing it.',`,
1664
+ ` review: review,`,
1665
+ ` stage: () async {`,
1666
+ ` await _stageGeneratedModel();`,
1667
+ ` return _adoptGeneratedModel();`,
1668
+ ` },`,
1669
+ ` );`,
1670
+ ` }`,
1671
+ ]),
1632
1672
  ` if (stage == 'acceptance') {`,
1633
1673
  ` return NomosModelAdoption.needsDecision(`,
1634
1674
  ` summary: 'This model would change prior business outcomes. Review the differences and record why that is intended.',`,
1635
1675
  ` review: review, accept: (reason) => _adoptGeneratedModel(reason));`,
1636
1676
  ` }`,
1637
1677
  ` final evolve = (report?['evolve'] as Map?)?.cast<String, Object?>();`,
1638
- ` final summary = stage == 'identity'`,
1678
+ ` final rawError = raw['error']?.toString().trim();`,
1679
+ ` final summary = rawError != null && rawError.isNotEmpty`,
1680
+ ` ? rawError`,
1681
+ ` : stage == 'identity'`,
1639
1682
  ` ? 'Sign in before adopting this model so the decision is accountable.'`,
1640
1683
  ` : report != null && report['sourceVerified'] != true`,
1641
1684
  ` ? 'The current history could not be verified. Nothing changed and the current model remains active.'`,
@@ -1647,6 +1690,20 @@ export function generateDartBridgeClient(
1647
1690
  ` return NomosModelAdoption.blocked(summary: summary, review: review);`,
1648
1691
  ` }`,
1649
1692
  ``,
1693
+ ...(opts.lawAssetKey === undefined
1694
+ ? []
1695
+ : [
1696
+ ` Future<void> _stageGeneratedModel() async {`,
1697
+ ` final archive = await rootBundle.load(${dartStr(opts.lawAssetKey)});`,
1698
+ ` await bridge.nomosInternalStageGeneratedModel(`,
1699
+ ` domain: ${dartStr(domain)},`,
1700
+ ` candidateHash: domainHash,`,
1701
+ ` lawUsdz: archive.buffer.asUint8List(`,
1702
+ ` archive.offsetInBytes, archive.lengthInBytes),`,
1703
+ ` );`,
1704
+ ` }`,
1705
+ ``,
1706
+ ]),
1650
1707
  ` Future<NomosModelRestoration> _restorePreviousGeneratedModel([String? because]) async {`,
1651
1708
  ` final raw = await bridge.nomosFrameworkRestorePreviousGeneratedModel(`,
1652
1709
  ` domain: ${dartStr(
@@ -2159,6 +2216,9 @@ export function generateDartBridgeClient(
2159
2216
  ? [`import 'dart:convert';`, ``]
2160
2217
  : []),
2161
2218
  `import '${supportImport}';`,
2219
+ ...(opts.lawAssetKey !== undefined
2220
+ ? [`import 'package:flutter/services.dart' show rootBundle;`]
2221
+ : []),
2162
2222
  // Layered-packages mode: the sibling types live in their OWN package — one barrel import
2163
2223
  // replaces both the relative types import and the relative framework import.
2164
2224
  ...(opts.typesImportUri !== undefined
@@ -2299,6 +2359,66 @@ export function generateDartBridgeClient(
2299
2359
  ` static Future<${className}> bind(NomosSession session, {String? actor${
2300
2360
  manualEncryptionPresent ? ", NomosKeyStore? keyStore" : ""
2301
2361
  }}) async {`,
2362
+ ` final candidate = ${className}(session, actor: actor${
2363
+ manualEncryptionPresent ? ", keyStore: keyStore" : ""
2364
+ });`,
2365
+ ` Future<void> adoptGeneratedModel() async {`,
2366
+ ` final adoption = await candidate._adoptGeneratedModel();`,
2367
+ ` if (adoption.status == NomosModelAdoptionStatus.adopted) return;`,
2368
+ ` throw DomainClientModelAdoptionRequired(`,
2369
+ ` expectedDomainKey: interface.domain,`,
2370
+ ` expectedDomainHash: interface.expectedDomainHash,`,
2371
+ ` status: adoption.status.name,`,
2372
+ ` summary: adoption.summary,`,
2373
+ ` review: DomainModelReview(`,
2374
+ ` checkedActions: adoption.review.checkedActions,`,
2375
+ ` candidateActions: adoption.review.candidateActions,`,
2376
+ ` sameOutcome: adoption.review.sameOutcome,`,
2377
+ ` differences: [`,
2378
+ ` for (final difference in adoption.review.differences)`,
2379
+ ` DomainModelDifference(`,
2380
+ ` kind: switch (difference.kind) {`,
2381
+ ` NomosModelDifferenceKind.rules => DomainModelDifferenceKind.rules,`,
2382
+ ` NomosModelDifferenceKind.outcome => DomainModelDifferenceKind.outcome,`,
2383
+ ` NomosModelDifferenceKind.evidence => DomainModelDifferenceKind.evidence,`,
2384
+ ` },`,
2385
+ ` area: difference.area,`,
2386
+ ` action: difference.action,`,
2387
+ ` summary: difference.summary,`,
2388
+ ` ),`,
2389
+ ` ],`,
2390
+ ` ),`,
2391
+ ...(opts.lawAssetKey === undefined
2392
+ ? [` stage: null,`]
2393
+ : [
2394
+ ` stage: adoption.status == NomosModelAdoptionStatus.needsStaging`,
2395
+ ` ? () => candidate._stageGeneratedModel()`,
2396
+ ` : null,`,
2397
+ ]),
2398
+ ` accept: adoption.status == NomosModelAdoptionStatus.needsDecision`,
2399
+ ` ? (reason) async {`,
2400
+ ` final accepted = await adoption.accept(reason);`,
2401
+ ` if (accepted.status != NomosModelAdoptionStatus.adopted) {`,
2402
+ ` throw StateError(accepted.summary);`,
2403
+ ` }`,
2404
+ ` }`,
2405
+ ` : null,`,
2406
+ ` );`,
2407
+ ` }`,
2408
+ ` try {`,
2409
+ ` await session.assertCompatible(interface);`,
2410
+ ` } on DomainClientSessionMismatch catch (mismatch) {`,
2411
+ ` // A deployed successor is deliberately invisible until the generated candidate proves it`,
2412
+ ` // against this workspace's exact history. Do that before rejecting the candidate for`,
2413
+ ` // expecting its own newer interface; all other bind failures retain their precise cause.`,
2414
+ ` if (!mismatch.canAttemptGeneratedModelAdoption) rethrow;`,
2415
+ ` await adoptGeneratedModel();`,
2416
+ ` }`,
2417
+ ` // Shape compatibility governs readable structure; exact package identity governs`,
2418
+ ` // which plans may author. A plan-only successor must therefore enter the same`,
2419
+ ` // proof-gated lifecycle even when every generated read type remains compatible.`,
2420
+ ` final law = await session.lawStatus(interface.expectedDomainHash);`,
2421
+ ` if (law['drifted'] == true) await adoptGeneratedModel();`,
2302
2422
  ` await session.assertCompatible(interface);`,
2303
2423
  ` // RELEASE COHERENCE: the cloud's kernel must support what this law needs (e.g. a compound-key query`,
2304
2424
  ` // needs 'compoundKeys'). Throws a typed KernelCoherenceError on a stale/mismatched cloud kernel,`,
@@ -2311,9 +2431,7 @@ export function generateDartBridgeClient(
2311
2431
  ownedScope
2312
2432
  )}, domain: ${dartStr(domain)}, domainHash: ${hashConst});`,
2313
2433
  ]),
2314
- ` return ${className}(session, actor: actor${
2315
- manualEncryptionPresent ? ", keyStore: keyStore" : ""
2316
- }).._preflighted = true${
2434
+ ` return candidate.._preflighted = true${
2317
2435
  ownedScope === undefined ? "" : ".._ownedIdentityReady = true"
2318
2436
  };`,
2319
2437
  ` }`,
@@ -2332,9 +2450,34 @@ export function generateDartBridgeClient(
2332
2450
  ` throw ArgumentError('${className}.open: pass a workspace: or ref: — the workspace to open and bind');`,
2333
2451
  ` }`,
2334
2452
  ` final session = await bridge.acquireSession(ws);`,
2335
- ` return bind(session, actor: actor${
2453
+ ...(opts.lawAssetKey === undefined
2454
+ ? []
2455
+ : [
2456
+ ` final law = await session.lawStatus(${hashConst});`,
2457
+ ` if (law['drifted'] == true) {`,
2458
+ ` if (bridge is! NomosSession) {`,
2459
+ ` throw StateError('Generated child model inheritance needs a bound parent session.');`,
2460
+ ` }`,
2461
+ ` final archive = await rootBundle.load(${dartStr(opts.lawAssetKey)});`,
2462
+ ` await bridge.nomosInternalEnsureGeneratedModelInChild(`,
2463
+ ` childWorkspace: ws,`,
2464
+ ` domain: interface.domain,`,
2465
+ ` candidateHash: ${hashConst},`,
2466
+ ` lawUsdz: archive.buffer.asUint8List(`,
2467
+ ` archive.offsetInBytes, archive.lengthInBytes),`,
2468
+ ` );`,
2469
+ ` }`,
2470
+ ]),
2471
+ ` for (var modelStep = 0; modelStep < 4; modelStep += 1) {`,
2472
+ ` try {`,
2473
+ ` return await bind(session, actor: actor${
2336
2474
  manualEncryptionPresent ? ", keyStore: keyStore" : ""
2337
2475
  });`,
2476
+ ` } on DomainClientModelAdoptionRequired catch (change) {`,
2477
+ ` await session.nomosInternalHandleModelChange(change);`,
2478
+ ` }`,
2479
+ ` }`,
2480
+ ` throw StateError('Generated model lifecycle did not settle after four governed steps.');`,
2338
2481
  ` }`,
2339
2482
  ``,
2340
2483
  ` /// The session workspace this client is bound to (from the [NomosSession] handle, when constructed with`,
@@ -326,7 +326,7 @@ function sampleOfZod(
326
326
  export function synthesizePayload(
327
327
  schema: z.ZodTypeAny,
328
328
  where: string,
329
- opts?: { voIncludeOptional?: boolean; example?: unknown },
329
+ opts?: { voIncludeOptional?: boolean; includeOptionalTopLevel?: boolean; example?: unknown },
330
330
  ): SynthesizedPayload {
331
331
  if (zodKind(schema) !== "object") {
332
332
  throw new Error(`codegen-proof: ${where} payload is not a zod object — the proof can only synthesize object payloads`);
@@ -348,8 +348,9 @@ export function synthesizePayload(
348
348
  for (const [name, raw] of Object.entries(zodObjectShape(schema))) {
349
349
  const f = raw as z.ZodTypeAny;
350
350
  const k = zodKind(f);
351
- if (k === "optional" || k === "default") continue; // optional → omit
352
351
  if (stamped.has(name)) continue; // `…At` → omit; the client stamps it
352
+ if (k === "default") continue; // defaulted fields exercise their declared default
353
+ if (k === "optional" && opts?.includeOptionalTopLevel !== true) continue;
353
354
  value[name] = sampleOfZod(f, name, where, opts?.voIncludeOptional === true); // path root = the payload field name
354
355
  }
355
356
  return { value, autoStamped };
@@ -361,11 +362,11 @@ export function synthesizePayload(
361
362
  export function synthesizeDirectivePayload(
362
363
  directive: Directive<any>,
363
364
  where: string,
364
- opts?: { voIncludeOptional?: boolean },
365
+ opts?: { voIncludeOptional?: boolean; includeOptionalTopLevel?: boolean; useDirectiveExample?: boolean },
365
366
  ): SynthesizedPayload {
366
367
  return synthesizePayload(directive.payloadSchema as z.ZodTypeAny, where, {
367
368
  ...opts,
368
- ...(directive.payloadExample === undefined
369
+ ...(directive.payloadExample === undefined || opts?.useDirectiveExample === false
369
370
  ? {}
370
371
  : { example: directive.payloadExample }),
371
372
  });
@@ -701,10 +702,11 @@ function probePlanOpsWithPayload(
701
702
  export function probePlanOps(
702
703
  dir: Directive<unknown>,
703
704
  where: string,
705
+ opts?: { includeOptionalTopLevel?: boolean; useDirectiveExample?: boolean },
704
706
  ): { ops: FieldOp[]; synth: SynthesizedPayload; probePayload: Record<string, unknown> } | undefined {
705
707
  let synth: SynthesizedPayload;
706
708
  try {
707
- synth = synthesizeDirectivePayload(dir, where);
709
+ synth = synthesizeDirectivePayload(dir, where, opts);
708
710
  } catch {
709
711
  return undefined;
710
712
  }
@@ -3235,9 +3237,14 @@ function generateSealedApplicationProof(
3235
3237
  : { applicationMaxChildWorkspacesPerUser: opts.applicationMaxChildWorkspacesPerUser }),
3236
3238
  });
3237
3239
  const standalone = orderedStandaloneCreates(mod)[0];
3238
- const businessBirth = primaryLegs.childBirth?.businessAggregateId !== undefined
3239
- ? primaryLegs.childBirth
3240
- : undefined;
3240
+ // A home/application domain normally carries an internal receiving-group self-birth before its public
3241
+ // business births. `childBirth` preserves that first leg for backwards-compatible proof metadata, but the
3242
+ // live generated application journey must exercise the first PUBLIC birth from the complete set. Looking
3243
+ // only at `childBirth` made a CO2 proof describe birthEstateWorkspace in its marker while executing an
3244
+ // unrelated AccessibleEstate write, so a broken estate genesis reached AppSim instead of failing compile.
3245
+ const businessBirth = (primaryLegs.childBirths ??
3246
+ (primaryLegs.childBirth === undefined ? [] : [primaryLegs.childBirth]))
3247
+ .find((birth) => birth.businessAggregateId !== undefined);
3241
3248
  const selectedBirthDirective = businessBirth === undefined
3242
3249
  ? undefined
3243
3250
  : (mod.applicationIntents ?? mod.directives).find(
@@ -3351,7 +3358,7 @@ function generateSealedApplicationProof(
3351
3358
 
3352
3359
  if (businessBirth !== undefined) {
3353
3360
  const businessPayload = Object.fromEntries(
3354
- Object.entries(businessBirth.birthPayload).filter(([field]) => !["principal", "bornAt", "ownerKeyHash", "birthCert"].includes(field)),
3361
+ Object.entries(businessBirth.birthPayload).filter(([field]) => !["principal", "bornAt"].includes(field)),
3355
3362
  );
3356
3363
  out.push(
3357
3364
  `const facts = ${JSON.stringify(businessPayload)};`,
@@ -202,6 +202,11 @@ function obligationFromJsonExpr(s: PayloadFieldSpec): string {
202
202
  `NmsInternalNomosRefs.create<${target}>(workspace: '', type: '${wireType}', id: ${v} as String)`;
203
203
  return s.optional ? `${raw} == null ? null : ${wrap(raw)}` : wrap(raw);
204
204
  }
205
+ case "addressRef": {
206
+ const target = s.dartType.replace(/^NomosRef<(.*)>$/, "$1");
207
+ const wrap = (v: string) => `NmsInternalNomosRefs.fromJson<${target}>(${v})`;
208
+ return s.optional ? `${raw} == null ? null : ${wrap(raw)}` : wrap(raw);
209
+ }
205
210
  case "versionedRef": {
206
211
  const target = s.versionedRefTarget as string;
207
212
  const series = s.versionedRefSeries as string;