@githolon/dsl 0.114.0 → 0.115.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 (67) hide show
  1. package/assets/operation-contract.wasm +0 -0
  2. package/dart/CHANGELOG.md +40 -0
  3. package/dart/lib/src/application_framework_calls.dart +7 -1
  4. package/dart/lib/src/console_enrolment.dart +7 -0
  5. package/dart/lib/src/console_http_io.dart +22 -15
  6. package/dart/lib/src/dev_cloud.dart +62 -0
  7. package/dart/lib/src/operation_receipts.dart +74 -7
  8. package/dart/lib/src/reception/dart_reception.dart +26 -6
  9. package/dart/lib/src/runner/node-entry.mjs +7129 -4424
  10. package/dart/lib/src/runtime_v2_transport.dart +3 -0
  11. package/dart/pubspec.lock +1 -1
  12. package/dart/pubspec.yaml +2 -2
  13. package/dart/test/console_enrolment_test.dart +47 -0
  14. package/dart/test/model_adoption_test.dart +49 -0
  15. package/dart/test/runtime_projections_test.dart +41 -0
  16. package/dart_types/CHANGELOG.md +24 -0
  17. package/dart_types/lib/src/approved_model.dart +78 -1
  18. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +528 -2
  19. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +24 -0
  20. package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pb.dart +1897 -112
  21. package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pb.dart +98 -0
  22. package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pb.dart +16 -0
  23. package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pb.dart +43 -2
  24. package/dart_types/pubspec.yaml +1 -1
  25. package/package.json +4 -2
  26. package/src/aggregate.ts +93 -0
  27. package/src/anonymous_value_interning.ts +205 -0
  28. package/src/baseline.ts +27 -0
  29. package/src/baseline_export.ts +432 -0
  30. package/src/baseline_import.ts +312 -0
  31. package/src/baseline_usda.ts +186 -0
  32. package/src/birth_key_rule.ts +472 -0
  33. package/src/births.ts +207 -24
  34. package/src/build_provenance.ts +10 -0
  35. package/src/business_birth.ts +50 -4
  36. package/src/check_package_main.ts +50 -6
  37. package/src/codegen_call_proto.ts +5 -18
  38. package/src/codegen_dart.ts +95 -3
  39. package/src/codegen_dart_client.ts +52 -6
  40. package/src/codegen_dart_facade.ts +57 -35
  41. package/src/codegen_dart_framework_calls.ts +20 -1
  42. package/src/codegen_proof.ts +5 -2
  43. package/src/codegen_result_proto.ts +8 -6
  44. package/src/codegen_result_usda.ts +4 -0
  45. package/src/codegen_ts.ts +109 -10
  46. package/src/compatibility_graph.ts +41 -4
  47. package/src/compatibility_usda.ts +11 -1
  48. package/src/compile_package_main.ts +214 -51
  49. package/src/composed_wire_identity.ts +24 -0
  50. package/src/directive.ts +6 -0
  51. package/src/domain_requirements.ts +75 -0
  52. package/src/engine_javascript.ts +12 -6
  53. package/src/fields.ts +109 -0
  54. package/src/identity_usda.ts +77 -51
  55. package/src/index.ts +25 -1
  56. package/src/intent.ts +5 -0
  57. package/src/law_graph.ts +142 -1
  58. package/src/law_graph_usda.ts +25 -141
  59. package/src/law_layer_prims.ts +199 -0
  60. package/src/law_unit_selection.ts +45 -0
  61. package/src/law_version.ts +107 -0
  62. package/src/package_config.ts +44 -0
  63. package/src/package_dependencies.ts +8 -0
  64. package/src/value_object.ts +6 -3
  65. package/src/wire.ts +2 -0
  66. package/src/wire_paths.ts +17 -0
  67. package/src/workspace_routing.ts +1 -1
@@ -699,6 +699,8 @@ export interface AggFieldSpec {
699
699
  dartType: string;
700
700
  /** A canonical semantic string wrapper whose wire representation remains String. */
701
701
  semanticString?: "sha256" | "git-object-id";
702
+ /** A typed id (`t.brandedId(Name)` / `t.uuid()`): the zero-cost extension type wrapping the wire String. */
703
+ brandedId?: string;
702
704
  enumValues?: string[];
703
705
  /** For an own-id string field represented as `NomosRef<TAggregate>`. */
704
706
  valueClass?: string;
@@ -923,6 +925,9 @@ function readKeyParamSpec(
923
925
  if (fieldSpec.semanticString === "git-object-id") {
924
926
  return { dartType: "GitObjectId", valueExpr: `${paramName}.value` };
925
927
  }
928
+ if (fieldSpec.brandedId !== undefined) {
929
+ return { dartType: fieldSpec.brandedId, valueExpr: `${paramName}.value` };
930
+ }
926
931
  return { dartType: "String", valueExpr: paramName };
927
932
  }
928
933
 
@@ -1032,7 +1037,9 @@ function aggFieldSpec(
1032
1037
  // field schema marked them `.optional()`.
1033
1038
  const optional = isCollectionKind(field.kind) ? false : field.isOptional;
1034
1039
  switch (field.kind) {
1035
- case "string":
1040
+ case "string": {
1041
+ // A TYPED ID field (`t.brandedId(Name)` / `t.uuid()`) reads as its extension type, never a String.
1042
+ const brand = valueClass === undefined ? brandedIdMetaOf(field.zod as z.ZodTypeAny) : undefined;
1036
1043
  return {
1037
1044
  name,
1038
1045
  wireName,
@@ -1042,9 +1049,10 @@ function aggFieldSpec(
1042
1049
  ? "Sha256Digest"
1043
1050
  : isGitObjectIdSchema(field.zod as z.ZodTypeAny)
1044
1051
  ? "GitObjectId"
1045
- : "String"
1052
+ : brand ?? "String"
1046
1053
  : `NomosRef<${valueClass}>`,
1047
1054
  optional,
1055
+ ...(brand !== undefined ? { brandedId: brand } : {}),
1048
1056
  ...(valueClass === undefined && isSha256DigestSchema(field.zod as z.ZodTypeAny)
1049
1057
  ? { semanticString: "sha256" as const }
1050
1058
  : {}),
@@ -1055,6 +1063,7 @@ function aggFieldSpec(
1055
1063
  ? { valueClass, valueAggregateType }
1056
1064
  : {}),
1057
1065
  };
1066
+ }
1058
1067
  case "json":
1059
1068
  // A NAMED VALUE OBJECT (`ValueObject.field()`) types as its generated Dart class
1060
1069
  // (typed decode via `.fromProjected`, the EvidenceRef pattern). Otherwise
@@ -3172,6 +3181,11 @@ function parseField(s: AggFieldSpec, aggregate: string): string {
3172
3181
  ? `() { final v = ${reader}; return v == null ? null : GitObjectId(v); }()`
3173
3182
  : `GitObjectId(${reader})`;
3174
3183
  }
3184
+ if (s.brandedId !== undefined) {
3185
+ return opt
3186
+ ? `() { final v = ${reader}; return v == null ? null : ${s.brandedId}(v); }()`
3187
+ : `${s.brandedId}(${reader})`;
3188
+ }
3175
3189
  return reader;
3176
3190
  }
3177
3191
  case "json":
@@ -3260,7 +3274,9 @@ function slotDecodeCallback(s: AggFieldSpec, mode: "aggregate" | "readModel"): s
3260
3274
  ? `(v) => Sha256Digest(v as String)`
3261
3275
  : s.semanticString === "git-object-id"
3262
3276
  ? `(v) => GitObjectId(v as String)`
3263
- : `(v) => v as String`;
3277
+ : s.brandedId !== undefined
3278
+ ? `(v) => ${s.brandedId}(v as String)`
3279
+ : `(v) => v as String`;
3264
3280
  case "json":
3265
3281
  if (s.voClass !== undefined) {
3266
3282
  return `(v) => ${s.voClass}.fromProjected((v as Map).cast<String, Object?>())`;
@@ -3413,6 +3429,11 @@ function parseReadModelField(s: AggFieldSpec, aggregate: string): string {
3413
3429
  ? `() { final v = ${reader}; return v == null ? null : GitObjectId(v); }()`
3414
3430
  : `GitObjectId(${reader})`;
3415
3431
  }
3432
+ if (s.brandedId !== undefined) {
3433
+ return opt
3434
+ ? `() { final v = ${reader}; return v == null ? null : ${s.brandedId}(v); }()`
3435
+ : `${s.brandedId}(${reader})`;
3436
+ }
3416
3437
  return reader;
3417
3438
  }
3418
3439
  case "json":
@@ -4993,6 +5014,65 @@ export interface GenerateDartDomainOptions {
4993
5014
  * because it was present in the composed law.
4994
5015
  */
4995
5016
  readonly additionalValueObjects?: readonly ValueObjectDescriptor[];
5017
+ /** Branded-id extension types another file (the package's `nomos_framework.dart`) declares because ≥2
5018
+ * domains use them; this domain references them via {@link frameworkImportUri}. */
5019
+ readonly skipBrandNames?: ReadonlySet<string>;
5020
+ /** Branded-id extension types this file declares for the package (the framework file's shared set). */
5021
+ readonly extraBrandNames?: readonly string[];
5022
+ }
5023
+
5024
+ /**
5025
+ * Every branded-id name (`brandedId(...)`, `t.brandedId(...)`, `t.uuid()`) whose Dart extension type this
5026
+ * domain's generated types reference: typed-id aggregate fields, top-level payload fields of its public
5027
+ * directives (business births render their public payload), and value-object fields. The package counts
5028
+ * these per domain so a brand used by two domains is declared ONCE, in the framework file.
5029
+ */
5030
+ export function dartBrandNamesForModule(mod: DomainModule): Set<string> {
5031
+ const brands = new Set<string>();
5032
+ const publicAggregates = mod.aggregates.filter(
5033
+ (a) => (a as { businessBirthInternal?: unknown }).businessBirthInternal !== true,
5034
+ );
5035
+ for (const agg of publicAggregates) {
5036
+ for (const f of Object.values(agg.fields as Record<string, Field>)) {
5037
+ if (f.kind !== "string") continue;
5038
+ const b = brandedIdMetaOf(f.zod as z.ZodTypeAny);
5039
+ if (b !== undefined) brands.add(b);
5040
+ }
5041
+ }
5042
+ const directives = (mod.applicationIntents ?? mod.directives)
5043
+ .filter((d) => (d as { businessBirthInternal?: unknown }).businessBirthInternal !== true)
5044
+ .map((d) => {
5045
+ const birth = (d as { businessBirth?: { publicPayloadSchema?: z.ZodTypeAny } }).businessBirth;
5046
+ return birth?.publicPayloadSchema === undefined ? d : { ...d, payloadSchema: birth.publicPayloadSchema };
5047
+ });
5048
+ for (const dir of directives) {
5049
+ const schema = (dir as { payloadSchema?: unknown }).payloadSchema;
5050
+ if (!schema || typeof schema !== "object" || zodTypeName(schema as z.ZodTypeAny) !== "ZodObject") continue;
5051
+ const rawShape = (zodDef(schema as z.ZodTypeAny) as { shape?: Record<string, z.ZodTypeAny> | (() => Record<string, z.ZodTypeAny>) }).shape;
5052
+ const shp = typeof rawShape === "function" ? rawShape() : rawShape ?? {};
5053
+ for (let zt of Object.values(shp)) {
5054
+ for (let hops = 0; hops < 8 && (zodTypeName(zt) === "ZodOptional" || zodTypeName(zt) === "ZodDefault" || zodTypeName(zt) === "ZodArray"); hops++) {
5055
+ zt = zodTypeName(zt) === "ZodArray" ? zodArrayElement(zt) : zodInnerType(zt);
5056
+ }
5057
+ const b = brandedIdMetaOf(zt);
5058
+ if (b !== undefined) brands.add(b);
5059
+ }
5060
+ }
5061
+ const walk = (f: ValueObjectFieldDescriptor): void => {
5062
+ if (f.type === "string" && f.brand !== undefined && f.idOf === undefined) brands.add(f.brand);
5063
+ else if (f.type === "array") walk(f.items);
5064
+ else if (f.type === "map") walk(f.values);
5065
+ };
5066
+ for (const d of [
5067
+ ...collectValueObjectDescriptors(publicAggregates),
5068
+ ...collectPayloadValueObjectDescriptors(directives),
5069
+ ]) {
5070
+ for (const f of Object.values(d.fields ?? {})) walk(f);
5071
+ for (const arm of Object.values(d.union ?? {})) {
5072
+ if ("fields" in arm) for (const f of Object.values(arm.fields)) walk(f);
5073
+ }
5074
+ }
5075
+ return brands;
4996
5076
  }
4997
5077
 
4998
5078
  /** Generate the full Dart source for one domain module. */
@@ -5098,6 +5178,16 @@ export function generateDartDomain(mod: DomainModule, opts: GenerateDartDomainOp
5098
5178
  else if (f.type === "array") walkUnitField(f.items);
5099
5179
  else if (f.type === "map") walkUnitField(f.values);
5100
5180
  };
5181
+ // TYPED-ID aggregate fields (`t.brandedId(Name)` / `t.uuid()`) declare their extension type with the
5182
+ // aggregate that owns them (skipping aggregates another file declares).
5183
+ for (const agg of mod.aggregates) {
5184
+ if (opts.skipAggregateIds?.has(agg.id) === true) continue;
5185
+ for (const f of Object.values(agg.fields as Record<string, Field>)) {
5186
+ if (f.kind !== "string") continue;
5187
+ const b = brandedIdMetaOf(f.zod as z.ZodTypeAny);
5188
+ if (b !== undefined) brandNames.add(b);
5189
+ }
5190
+ }
5101
5191
  for (const d of localValueObjects) {
5102
5192
  for (const f of Object.values(d.fields ?? {})) walkUnitField(f);
5103
5193
  for (const arm of Object.values(d.union ?? {})) {
@@ -5128,6 +5218,8 @@ export function generateDartDomain(mod: DomainModule, opts: GenerateDartDomainOp
5128
5218
  } catch { /* an unsupported payload shape is reported by the payload emitter, not here */ }
5129
5219
  }
5130
5220
  }
5221
+ for (const b of opts.skipBrandNames ?? []) brandNames.delete(b);
5222
+ for (const b of opts.extraBrandNames ?? []) brandNames.add(b);
5131
5223
  const unitBlock = [...unitNames].sort().map((u) => [
5132
5224
  `/// UNIT \`${u}\` — a ZERO-COST wrapper over the wire's plain JSON number (Dart extension`,
5133
5225
  `/// type): passing a bare double (or another unit) where [${u}] is required is a COMPILE`,
@@ -1,3 +1,4 @@
1
+ import { BUSINESS_BIRTH_KEY_PAYLOAD_FIELD } from "./births.js";
1
2
  import { dartRefusalDecoders } from "./codegen_refusal.js";
2
3
  // NOMOS — Nomos Sovereign: participants act · verify · remember LOCALLY; hosted
3
4
  // remotes are replaceable custody/transport, not truth. ⇒ ONE Nomos GitHolon
@@ -99,7 +100,7 @@ import { publicClientInterface, type DomainInterface } from "./compatibility_gra
99
100
  import { emitApplicationCompatibilityUsda } from "./compatibility_usda.js";
100
101
  import { BUSINESS_CORRECTION_DIRECTIVE } from "./framework/business_corrections.js";
101
102
  import { deriveDirectiveRecordTarget } from "./workspace_routing.js";
102
- import { voMetaOf } from "./value_object.js";
103
+ import { brandedIdMetaOf, voMetaOf } from "./value_object.js";
103
104
  import {
104
105
  buildNomosDescribeModel,
105
106
  type NomosDescribeModel,
@@ -389,6 +390,11 @@ function dartKeyType(
389
390
  if (f?.kind === "string" && isSha256DigestSchema(f.zod as z.ZodTypeAny)) {
390
391
  return "Sha256Digest";
391
392
  }
393
+ // A typed-id key (`t.brandedId(Name)` / `t.uuid()`) is its extension type, never a bare String.
394
+ if (f?.kind === "string" && !key.includes(".")) {
395
+ const brand = brandedIdMetaOf(f.zod as z.ZodTypeAny);
396
+ if (brand !== undefined) return brand;
397
+ }
392
398
  if (f?.kind === "string" && isGitObjectIdSchema(f.zod as z.ZodTypeAny)) {
393
399
  return "GitObjectId";
394
400
  }
@@ -435,6 +441,13 @@ function dartKeyExpr(agg: AggregateHandle | undefined, key: string): string {
435
441
  ) {
436
442
  return `${param}.value`;
437
443
  }
444
+ if (
445
+ field?.kind === "string" &&
446
+ !key.includes(".") &&
447
+ brandedIdMetaOf(field.zod as z.ZodTypeAny) !== undefined
448
+ ) {
449
+ return `${param}.value`;
450
+ }
438
451
  if (field?.kind !== "ref") return param;
439
452
  return field.foreignRef !== undefined
440
453
  ? `NmsInternalNomosRefs.id(${param}.ref)`
@@ -491,6 +504,8 @@ function dartUsdaBasePresentValue(
491
504
  ? `Sha256Digest(${value})`
492
505
  : spec.semanticString === "git-object-id"
493
506
  ? `GitObjectId(${value})`
507
+ : spec.brandedId !== undefined
508
+ ? `${spec.brandedId}(${value})`
494
509
  : value;
495
510
  }
496
511
  case "ref": {
@@ -1305,7 +1320,7 @@ export function generateDartDomainClient(
1305
1320
  const compilerInternal = sourceTrustViaIds.has(directive.id);
1306
1321
  const businessBirth = (
1307
1322
  directive as unknown as {
1308
- businessBirth?: { publicPayloadSchema: z.ZodTypeAny };
1323
+ businessBirth?: { publicPayloadSchema: z.ZodTypeAny; keyField?: string };
1309
1324
  }
1310
1325
  ).businessBirth;
1311
1326
  const specs = introspectPayload(
@@ -1360,6 +1375,15 @@ export function generateDartDomainClient(
1360
1375
  arguments_.push("principal", "bornAt");
1361
1376
  access.set("principal", { value: "nmsInternalBirthPrincipal" });
1362
1377
  access.set("bornAt", { value: "nmsInternalBornAt" });
1378
+ // A minted birth's law admits a key the client reserved ahead of dispatch (CAP-804).
1379
+ if (businessBirth.keyField === undefined) {
1380
+ extraParameters.push("String? nmsInternalBirthKey");
1381
+ arguments_.push("nmsInternalBirthKey");
1382
+ access.set(BUSINESS_BIRTH_KEY_PAYLOAD_FIELD, {
1383
+ value: "nmsInternalBirthKey!",
1384
+ present: "nmsInternalBirthKey != null",
1385
+ });
1386
+ }
1363
1387
  }
1364
1388
  for (const input of dartBusinessRecordInputs(directive, mod.aggregates)) {
1365
1389
  const origin = `NmsInternalRecordOrigins.require(payload.${input.argument})`;
@@ -1954,7 +1978,8 @@ export function generateDartDomainClient(
1954
1978
  domain: string;
1955
1979
  rootId: string;
1956
1980
  namespace: string;
1957
- keyField: string;
1981
+ keyField?: string;
1982
+ keyType?: { type: string; brand: string };
1958
1983
  seedDirectiveId?: string;
1959
1984
  genesisDirectiveIds?: readonly string[];
1960
1985
  publicPayloadSchema: z.ZodTypeAny;
@@ -2010,6 +2035,18 @@ export function generateDartDomainClient(
2010
2035
  const birthSpecsByName = new Map(
2011
2036
  birthSpecs.map((spec) => [spec.name, spec])
2012
2037
  );
2038
+ // A minted birth (the default) has no business key; an owned one seals to the child's address
2039
+ // before dispatch, so it reserves its key from the kernel's own minter and hands it to the law.
2040
+ const mintedBirth = businessBirth.keyField === undefined;
2041
+ const birthKeyPrelude = !mintedBirth
2042
+ ? []
2043
+ : ownedBirthDescriptor === undefined
2044
+ ? [` const String? nmsInternalBirthKey = null;`]
2045
+ : [
2046
+ ` final String? nmsInternalBirthKey = await session.mintId(${dartStr(
2047
+ businessBirth.aggregateId
2048
+ )});`,
2049
+ ];
2013
2050
  const ownedBirthPrelude =
2014
2051
  ownedBirthDescriptor === undefined
2015
2052
  ? []
@@ -2017,9 +2054,17 @@ export function generateDartDomainClient(
2017
2054
  ` final nmsInternalOwnedKey = await session.nomosOwnedBirthScopeKey(`,
2018
2055
  ` ${dartStr(ownedBirthDescriptor.scope)}, ${dartStr(
2019
2056
  businessBirth.namespace
2020
- )}, payload.${dartMemberName(businessBirth.keyField)}, ${dartStr(
2021
- birthRuntimeType!
2022
- )}, ${dartStr(businessBirth.rootId)});`,
2057
+ )}, ${
2058
+ mintedBirth
2059
+ ? "nmsInternalBirthKey!"
2060
+ : `payload.${dartMemberName(businessBirth.keyField!)}${
2061
+ businessBirth.keyType?.type === "brandedId" || businessBirth.keyType?.type === "uuid"
2062
+ ? ".value"
2063
+ : ""
2064
+ }`
2065
+ }, ${dartStr(birthRuntimeType!)}, ${dartStr(
2066
+ businessBirth.rootId
2067
+ )});`,
2023
2068
  ` final nmsInternalOwnedKeySubject = nmsInternalOwnedKey.ownedKeySubject;`,
2024
2069
  ` if (nmsInternalOwnedKeySubject == null || nmsInternalOwnedKeySubject.isEmpty) {`,
2025
2070
  ` throw StateError('Nomos did not return the framework-owned device identity for .owned() custody');`,
@@ -2148,6 +2193,7 @@ export function generateDartDomainClient(
2148
2193
  ),
2149
2194
  ]),
2150
2195
  ` final bornAt = DateTime.now().toUtc().toIso8601String();`,
2196
+ ...birthKeyPrelude,
2151
2197
  ...ownedBirthPrelude,
2152
2198
  ...birthSealPrelude,
2153
2199
  ...(birthEncryption === undefined
@@ -5,6 +5,7 @@ import { refusalContracts, refusalType, refusalDecoder } from "./codegen_refusal
5
5
  // application contract: business inputs in, business ids/models out. It deliberately has no public
6
6
  // session/session/workspace/actor/sync surface.
7
7
  import type { z } from "zod";
8
+ import { brandedIdMetaOf } from "./value_object.js";
8
9
 
9
10
  import type { AggregateHandle } from "./aggregate.js";
10
11
  import type { Directive } from "./directive.js";
@@ -153,6 +154,11 @@ function keyType(
153
154
  ) {
154
155
  return "GitObjectId";
155
156
  }
157
+ // A typed-id key (`t.brandedId(Name)` / `t.uuid()`) is its extension type, never a bare String.
158
+ if (field?.kind === "string" && !key.includes(".")) {
159
+ const brand = brandedIdMetaOf(field.zod as z.ZodTypeAny);
160
+ if (brand !== undefined) return brand;
161
+ }
156
162
  return "String";
157
163
  }
158
164
 
@@ -381,7 +387,7 @@ export function generateDartApplication(
381
387
  const source = aggregate.fields[name]!;
382
388
  const value = source.kind === "enum" ? `${access}?.wire`
383
389
  : source.kind === "ref" ? `${access}?.id`
384
- : source.kind === "string" && (isSha256DigestSchema(source.zod) || isGitObjectIdSchema(source.zod)) ? `${access}?.value`
390
+ : source.kind === "string" && (isSha256DigestSchema(source.zod) || isGitObjectIdSchema(source.zod) || brandedIdMetaOf(source.zod as z.ZodTypeAny) !== undefined) ? `${access}?.value`
385
391
  : access;
386
392
  return ` ${member(name)}: ${value},`;
387
393
  }),
@@ -900,7 +906,9 @@ export function generateDartApplication(
900
906
  aggregateId: string;
901
907
  namespace: string;
902
908
  rootId: string;
903
- keyField: string;
909
+ keyMode?: "minted" | "field";
910
+ keyField?: string;
911
+ keyType?: { type: string; brand: string };
904
912
  };
905
913
  birthsChild?: boolean;
906
914
  }
@@ -993,39 +1001,53 @@ export function generateDartApplication(
993
1001
  ` return _nmsReception.fetchWorkspace(reference.workspace);`,
994
1002
  ` }`,
995
1003
  ``,
996
- ` /// Open the ${birth.aggregateId} whose ${birth.keyField} is [key].`,
997
- ` /// THE OFFICE NAMES THE WORKSPACE this key addresses: the derivation is law,`,
998
- ` /// and a generated copy of it would silently address a different workspace`,
999
- ` /// the moment either side moved.`,
1000
- ` Future<${targetApplication}> open(String key) async => _nmsOpenReference(`,
1001
- ` NmsInternalNomosRefs.create<${rootType}>(`,
1002
- ` workspace: await _nmsReception.keyedBirthWorkspace(`,
1003
- ` namespace: ${JSON.stringify(birth.namespace)}, key: key,`,
1004
- ` ),`,
1005
- ` type: ${JSON.stringify(birth.aggregateId)},`,
1006
- ` id: ${JSON.stringify(birth.rootId)},`,
1007
- ` ),`,
1008
- ` );`,
1009
- ``,
1010
- ` /// Fetch the ${birth.aggregateId} whose ${birth.keyField} is [key] onto this device now,`,
1011
- ` /// opening and reading nothing: its placement's follow mounts the provider's head in`,
1012
- ` /// the background. Completes once that follow exists (e.g. prefetching every workspace`,
1013
- ` /// a list names, all at once, so each is local before it is tapped).`,
1014
- ` Future<void> fetch(String key) async => _nmsReception.fetchWorkspace(`,
1015
- ` await _nmsReception.keyedBirthWorkspace(`,
1016
- ` namespace: ${JSON.stringify(birth.namespace)}, key: key,`,
1017
- ` ),`,
1018
- ` );`,
1019
- ``,
1020
- ` /// Kernel-owned progress updates while [open] prepares this workspace.`,
1021
- ` /// ONE LANE: the stream is the whole surface. A synchronous snapshot`,
1022
- ` /// would have to answer before the office has named the workspace, so`,
1023
- ` /// it could only ever answer null or hold a cached name of its own.`,
1024
- ` Stream<ApplicationWorkspaceOpenProgress> watchOpenProgress(String key) =>`,
1025
- ` Stream<String>.fromFuture(_nmsReception.keyedBirthWorkspace(`,
1026
- ` namespace: ${JSON.stringify(birth.namespace)}, key: key,`,
1027
- ` )).asyncExpand(_nmsReception.streams.workspaceOpenProgress);`,
1028
- ``,
1004
+ // Open/fetch BY KEY exist only for a birth keyed by a declared business fact. A minted birth
1005
+ // (the default) has no business key: its address is the NomosRef its birth returned.
1006
+ // The key param is the key field's TYPED ID (its extension type, declared with the birth payload in
1007
+ // this domain's types); `.value` is its wire string.
1008
+ ...(birth.keyField === undefined
1009
+ ? []
1010
+ : (() => {
1011
+ const typedKey = birth.keyType !== undefined &&
1012
+ (birth.keyType.type === "brandedId" || birth.keyType.type === "uuid");
1013
+ const keyParam = typedKey ? `${birth.keyType!.brand} key` : "String key";
1014
+ const keyWire = typedKey ? "key.value" : "key";
1015
+ return [
1016
+ ` /// Open the ${birth.aggregateId} whose ${birth.keyField} is [key].`,
1017
+ ` /// THE OFFICE NAMES THE WORKSPACE this key addresses: the derivation is law,`,
1018
+ ` /// and a generated copy of it would silently address a different workspace`,
1019
+ ` /// the moment either side moved.`,
1020
+ ` Future<${targetApplication}> open(${keyParam}) async => _nmsOpenReference(`,
1021
+ ` NmsInternalNomosRefs.create<${rootType}>(`,
1022
+ ` workspace: await _nmsReception.keyedBirthWorkspace(`,
1023
+ ` namespace: ${JSON.stringify(birth.namespace)}, key: ${keyWire},`,
1024
+ ` ),`,
1025
+ ` type: ${JSON.stringify(birth.aggregateId)},`,
1026
+ ` id: ${JSON.stringify(birth.rootId)},`,
1027
+ ` ),`,
1028
+ ` );`,
1029
+ ``,
1030
+ ` /// Fetch the ${birth.aggregateId} whose ${birth.keyField} is [key] onto this device now,`,
1031
+ ` /// opening and reading nothing: its placement's follow mounts the provider's head in`,
1032
+ ` /// the background. Completes once that follow exists (e.g. prefetching every workspace`,
1033
+ ` /// a list names, all at once, so each is local before it is tapped).`,
1034
+ ` Future<void> fetch(${keyParam}) async => _nmsReception.fetchWorkspace(`,
1035
+ ` await _nmsReception.keyedBirthWorkspace(`,
1036
+ ` namespace: ${JSON.stringify(birth.namespace)}, key: ${keyWire},`,
1037
+ ` ),`,
1038
+ ` );`,
1039
+ ``,
1040
+ ` /// Kernel-owned progress updates while [open] prepares this workspace.`,
1041
+ ` /// ONE LANE: the stream is the whole surface. A synchronous snapshot`,
1042
+ ` /// would have to answer before the office has named the workspace, so`,
1043
+ ` /// it could only ever answer null or hold a cached name of its own.`,
1044
+ ` Stream<ApplicationWorkspaceOpenProgress> watchOpenProgress(${keyParam}) =>`,
1045
+ ` Stream<String>.fromFuture(_nmsReception.keyedBirthWorkspace(`,
1046
+ ` namespace: ${JSON.stringify(birth.namespace)}, key: ${keyWire},`,
1047
+ ` )).asyncExpand(_nmsReception.streams.workspaceOpenProgress);`,
1048
+ ``,
1049
+ ];
1050
+ })()),
1029
1051
  ]);
1030
1052
  addCollectionMember(birth.aggregateId, "mine", [
1031
1053
  ` /// The ${birth.aggregateId} lives founded by the signed-in person.`,
@@ -1,3 +1,4 @@
1
+ import { assertDomainRequirements, type DomainUnitRequirement } from "./domain_requirements.js";
1
2
  import type { z } from "zod";
2
3
 
3
4
  import type { Directive } from "./directive.js";
@@ -177,6 +178,12 @@ export function generateDartApplicationFrameworkCalls(
177
178
  readonly lawUnitSet: string;
178
179
  /** The compiled package closure (`build/<name>.law.usdz`): the bytes a device stages. Not what a target serves. */
179
180
  readonly lawPackageSha256: string;
181
+ /**
182
+ * THE PER-DOMAIN REQUIREMENTS (CAP-666): per application domain key, its unit hash and the identity of its
183
+ * generated compatibility requirement. Absent or empty emits an empty table (a package with no application
184
+ * interface); the getter itself is always emitted, since the runtime interface is exhaustive.
185
+ */
186
+ readonly domainRequirements?: readonly DomainUnitRequirement[];
180
187
  },
181
188
  ): string | undefined {
182
189
  for (const [name, value] of [["law unit set", descriptor.lawUnitSet], ["package", descriptor.lawPackageSha256]] as const) {
@@ -184,6 +191,8 @@ export function generateDartApplicationFrameworkCalls(
184
191
  throw new Error(`Dart framework call codegen: the application's ${name} identity must be 64 lowercase hex, not '${value}'`);
185
192
  }
186
193
  }
194
+ const domainRequirements = [...(descriptor.domainRequirements ?? [])].sort((a, b) => a.domainKey < b.domainKey ? -1 : a.domainKey > b.domainKey ? 1 : 0);
195
+ assertDomainRequirements(domainRequirements);
187
196
  const frameworkEnumDartTypes = collectFixedFrameworkDartEnums().dartTypes;
188
197
  const fixedEmitted = FIXED_CALLS.flatMap((definition) => laws.flatMap((law) => {
189
198
  const declares = law.graph.nodes.some((node) =>
@@ -622,7 +631,7 @@ export function generateDartApplicationFrameworkCalls(
622
631
  `import 'package:fixnum/fixnum.dart' show Int64;`,
623
632
  `import 'operation_payloads.pb.dart' as _nmsop;`,
624
633
  `import 'package:nomos_types/nomos_kernel_protocol.dart' show BirthCertificate;`,
625
- `import 'package:nomos_types/nomos_types.dart' show NomosSlot, Sha256Digest;`,
634
+ `import 'package:nomos_types/nomos_types.dart' show NomosDomainRequirement, NomosSlot, Sha256Digest;`,
626
635
  `import 'package:nomos_client/src/application_framework_calls.dart';`,
627
636
  `import 'package:nomos_client/src/generated_call.dart';`,
628
637
  ``,
@@ -651,6 +660,16 @@ export function generateDartApplicationFrameworkCalls(
651
660
  ` String get lawUnitSet => ${JSON.stringify(descriptor.lawUnitSet)};`,
652
661
  ` @override`,
653
662
  ` String get lawPackageSha256 => ${JSON.stringify(descriptor.lawPackageSha256)};`,
663
+ ` @override`,
664
+ ...(domainRequirements.length === 0
665
+ ? [` Map<String, NomosDomainRequirement> get domainRequirements => const {};`]
666
+ : [
667
+ ` Map<String, NomosDomainRequirement> get domainRequirements => const {`,
668
+ ...domainRequirements.map((requirement) =>
669
+ ` ${JSON.stringify(requirement.domainKey)}: NomosDomainRequirement(domainKey: ${JSON.stringify(requirement.domainKey)}, `
670
+ + `unitSha256: ${JSON.stringify(requirement.unitSha256)}, requirementSha256: ${JSON.stringify(requirement.requirementSha256)}),`),
671
+ ` };`,
672
+ ]),
654
673
  ``,
655
674
  ...methods,
656
675
  `}`,
@@ -32,6 +32,7 @@
32
32
  * Deterministic codegen: the emitted bytes are a pure function of the modules +
33
33
  * package name + domain hash — no timestamps or generation-time randomness.
34
34
  */
35
+ import { BUSINESS_BIRTH_KEY_PAYLOAD_FIELD } from "./births.js";
35
36
  import type { z } from "zod";
36
37
 
37
38
  import { BUSINESS_BIRTH_SEED_PAYLOAD_FIELD, type AggregateHandle } from "./aggregate.js";
@@ -2836,10 +2837,12 @@ function attachBusinessEncryption(
2836
2837
  const directive = (mod?.directives as Directive<unknown>[] | undefined)?.find(
2837
2838
  (candidate) => candidate.id === leg.birthDirectiveId,
2838
2839
  ) as (Directive<unknown> & {
2839
- businessBirth?: { namespace?: string; keyField?: string };
2840
+ businessBirth?: { namespace?: string; keyField?: string; keyMode?: string };
2840
2841
  }) | undefined;
2841
2842
  const namespace = directive?.businessBirth?.namespace;
2842
- const keyField = directive?.businessBirth?.keyField;
2843
+ // A minted birth's key rides the hidden reserved-key field (CAP-804).
2844
+ const keyField = directive?.businessBirth?.keyField
2845
+ ?? (directive?.businessBirth?.keyMode === "minted" ? BUSINESS_BIRTH_KEY_PAYLOAD_FIELD : undefined);
2843
2846
  const offered = packageInterface[leg.parentDomain];
2844
2847
  const offeredAggregate = offered === undefined
2845
2848
  ? undefined
@@ -1,4 +1,5 @@
1
1
  import { admitsNull, isGitObjectIdSchema, isSha256DigestSchema, type Field } from "./fields.js";
2
+ import { brandedIdMetaOf } from "./value_object.js";
2
3
  import type {
3
4
  LawAnonymousValueFieldNode,
4
5
  LawAnonymousValueTypeNode,
@@ -125,12 +126,13 @@ function authoredFieldValue(
125
126
  return `foreignRef<${target}>(${holon}, ${JSON.stringify(field.foreignRef.targetAggregateId)}, ${selected}, ${foreignWorkspace})`;
126
127
  }
127
128
  switch (field.kind) {
128
- case "string":
129
- return isSha256DigestSchema(field.zod)
130
- ? `sha256Digest(${selected})`
131
- : isGitObjectIdSchema(field.zod)
132
- ? `gitObjectId(${selected})`
133
- : selected;
129
+ case "string": {
130
+ if (isSha256DigestSchema(field.zod)) return `sha256Digest(${selected})`;
131
+ if (isGitObjectIdSchema(field.zod)) return `gitObjectId(${selected})`;
132
+ // A typed id field (`t.brandedId(Name)` / `t.uuid()`): the wire string IS the id (nominal only).
133
+ const brand = brandedIdMetaOf(field.zod as never);
134
+ return brand === undefined ? selected : `(${selected} as ${brand})`;
135
+ }
134
136
  case "position": return selected;
135
137
  case "ref":
136
138
  return field.foreignRef === undefined &&
@@ -15,6 +15,7 @@ import {
15
15
  zodKind,
16
16
  zodObjectShape,
17
17
  } from "./zod_introspection.js";
18
+ import { brandedIdMetaOf } from "./value_object.js";
18
19
 
19
20
  export interface TsResultField {
20
21
  readonly name: string;
@@ -391,6 +392,9 @@ const decode = (field: Field, expression: string): string => {
391
392
  ? `sha256Digest(_nmsUsdaStringValue(${expression}))`
392
393
  : isGitObjectIdSchema(field.zod)
393
394
  ? `gitObjectId(_nmsUsdaStringValue(${expression}))`
395
+ : brandedIdMetaOf(field.zod as z.ZodTypeAny) !== undefined
396
+ // A typed id field: the wire string IS the id; the brand is nominal only.
397
+ ? `(_nmsUsdaStringValue(${expression}) as ${brandedIdMetaOf(field.zod as z.ZodTypeAny)})`
394
398
  : `_nmsUsdaStringValue(${expression})`;
395
399
  case "position":
396
400
  return `_nmsUsdaStringValue(${expression})`;