@githolon/dsl 0.103.4 → 0.103.5

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 +142 -40
  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
package/src/births.ts CHANGED
@@ -7,7 +7,7 @@ import { z } from "zod";
7
7
  import type { AggregateHandle } from "./aggregate.js";
8
8
  import type { AggregateRef } from "./authoring.js";
9
9
  import type { Ports } from "./ctx.js";
10
- import type { Field, FieldValue } from "./fields.js";
10
+ import type { Field, FieldValue, NomosRefValue } from "./fields.js";
11
11
 
12
12
  /**
13
13
  * THE BIRTH EFFECT — birth is an EXPLICIT, DECLARED kernel primitive (G3, the unix model).
@@ -76,6 +76,9 @@ export interface BusinessBirthDecl<
76
76
  readonly payloadSchema: z.ZodObject<z.ZodRawShape> & z.ZodType<P>;
77
77
  /** A stable business key the caller already knows (an estate reference, account number, etc.). */
78
78
  readonly key: (payload: P) => string;
79
+ /** The complete custody address this declaration gives the born root.
80
+ * Carry it in parent projections instead of reconstructing workspace names in tenant code. */
81
+ readonly reference: (payload: P) => NomosRefValue;
79
82
  /** The declared business fact used by {@link key}; retained for generated clients to pre-address custody. */
80
83
  readonly keyField: string;
81
84
  /** Framework-derived initial authoring. Tenant code marks aggregate fields `.fromBirth()` instead. */
@@ -192,11 +195,23 @@ export function birth<F extends Record<string, Field>>(
192
195
  );
193
196
  }
194
197
  type Payload = BusinessBirthPayload<F>;
198
+ const aggregateNoun = aggregate.id
199
+ .replace(/([a-z0-9])([A-Z])/g, "$1-$2")
200
+ .toLowerCase();
201
+ const namespace = (spec.namespace ?? aggregateNoun)
202
+ .replace(/([a-z0-9])([A-Z])/g, "$1-$2")
203
+ .toLowerCase();
204
+ const rootId = `${aggregateNoun}:self`;
195
205
  const declaration = {
196
206
  __isBusinessBirth: true,
197
207
  aggregate,
198
208
  payloadSchema,
199
209
  key: (payload: Payload) => String(payload[spec.key]),
210
+ reference: (payload: Payload): NomosRefValue => ({
211
+ workspace: workspaceNameForKey(namespace, String(payload[spec.key])),
212
+ type: aggregate.id,
213
+ id: rootId,
214
+ }),
200
215
  keyField: spec.key,
201
216
  plan: (root: AggregateRef<F>, payload: Payload, ctx: Ports) => {
202
217
  const writable = root as unknown as { set(field: string, value: unknown): unknown };
@@ -50,18 +50,34 @@ import {
50
50
  } from "./framework/signing_identity.js";
51
51
  import {
52
52
  DomainInstallExecutor,
53
+ currentLawReference,
53
54
  domainInstallGenesisOffer,
54
55
  } from "./framework/domain_lifecycle.js";
55
56
 
56
- /** Private recipe selectors. The kernel resolves these against the parent's current installed law. */
57
- export const CURRENT_LAW_PREFIX = "nomos:current-law:";
58
57
  /** @internal Compiler-only domain enumeration. It carries no allowance or authority. */
59
58
  export const BUSINESS_BIRTH_POLICY_DOMAINS_EXPORT = "__nomos_birth_policy_domains_c9f14a";
60
59
 
60
+ /**
61
+ * Typed compiler seam carried by an already-lowered birth consequence.
62
+ *
63
+ * A narrow plan can retain a public intent whose closure contains a child birth
64
+ * without retaining the child module's top-level declaration export. The
65
+ * symbol keeps the compiler's namespace binding attached to that executable
66
+ * value, without another string-key convention or a scan of function source.
67
+ */
68
+ export const BUSINESS_BIRTH_POLICY_BINDING = Symbol("nomos.business-birth-policy-binding");
69
+
70
+ export interface BusinessBirthPolicyBinding {
71
+ readonly [BUSINESS_BIRTH_POLICY_BINDING]: (domains: readonly string[]) => void;
72
+ }
73
+
61
74
  export interface BusinessBirthMetadata {
62
75
  readonly aggregateId: string;
63
76
  /** Owning law domain for closure-safe cross-workspace intent linking. */
64
77
  readonly domain: string;
78
+ /** The born root uses Nomos-owned custody, so the compiler must type the private
79
+ * authoring facts carried beside (but never exposed as) the tenant payload. */
80
+ readonly owned: boolean;
65
81
  readonly publicPayloadSchema: z.ZodObject<z.ZodRawShape>;
66
82
  readonly rootId: string;
67
83
  readonly namespace: string;
@@ -75,7 +91,8 @@ export interface BusinessBirthMetadata {
75
91
  readonly founderScope: string;
76
92
  }
77
93
 
78
- export interface BusinessBirthDirective extends Directive<Record<string, unknown>> {
94
+ export interface BusinessBirthDirective
95
+ extends Directive<Record<string, unknown>>, BusinessBirthPolicyBinding {
79
96
  readonly businessBirth: BusinessBirthMetadata;
80
97
  }
81
98
 
@@ -96,11 +113,8 @@ function internalSurface<T extends object>(value: T): T & { readonly businessBir
96
113
  const INTERNAL_FIELDS = [
97
114
  "principal",
98
115
  "bornAt",
99
- "ownerKeyHash",
100
- "birthCert",
101
116
  BUSINESS_BIRTH_SEED_PAYLOAD_FIELD,
102
117
  ] as const;
103
- const sha256Hex = z.string().regex(/^[0-9a-f]{64}$/);
104
118
 
105
119
  /**
106
120
  * Every business-born workspace verifies its own travelling birth certificate against its warrant owner.
@@ -121,6 +135,14 @@ function businessBirthsOf(mod: Record<string, unknown>): BusinessBirthDecl[] {
121
135
  );
122
136
  }
123
137
 
138
+ function businessBirthPolicyBindingsOf(mod: Record<string, unknown>): BusinessBirthPolicyBinding[] {
139
+ return Object.values(mod).filter((value): value is BusinessBirthPolicyBinding =>
140
+ value !== null &&
141
+ typeof value === "object" &&
142
+ BUSINESS_BIRTH_POLICY_BINDING in value
143
+ );
144
+ }
145
+
124
146
  // A declared birth remains reusable as framework law, but once a wider business
125
147
  // intent composes it there must not also be a generated direct-birth lane. That
126
148
  // second lane could skip the intent's parent-workspace consequences and let two
@@ -155,8 +177,6 @@ function lowerOne<P extends Record<string, unknown>, F extends Record<string, Fi
155
177
  const seedSchema = publicSchema.extend({
156
178
  principal: z.string().min(1),
157
179
  bornAt: z.string().min(1),
158
- ownerKeyHash: sha256Hex.optional(),
159
- birthCert: z.string().optional(),
160
180
  });
161
181
  const parentSchema = seedSchema.extend({
162
182
  ...(encryptedBirth ? { [BUSINESS_BIRTH_SEED_PAYLOAD_FIELD]: z.string().min(1) } : {}),
@@ -240,12 +260,12 @@ function lowerOne<P extends Record<string, unknown>, F extends Record<string, Fi
240
260
  const chain: Array<Record<string, unknown>> = [
241
261
  domainInstallGenesisOffer(
242
262
  DomainInstallExecutor.StageZero,
243
- `${CURRENT_LAW_PREFIX}nomos`,
263
+ currentLawReference("nomos"),
244
264
  founder,
245
265
  ),
246
266
  domainInstallGenesisOffer(
247
267
  DomainInstallExecutor.LifecycleController,
248
- `${CURRENT_LAW_PREFIX}${domain}`,
268
+ currentLawReference(domain),
249
269
  founder,
250
270
  ),
251
271
  ...(decl.__nomosBirthPolicyDomains ?? initialBirthPolicyDomains).map((policyDomain) => ({
@@ -275,21 +295,6 @@ function lowerOne<P extends Record<string, unknown>, F extends Record<string, Fi
275
295
  const v = ap?.[k];
276
296
  return typeof v === "string" && v.length > 0 ? v : undefined;
277
297
  };
278
- let verified = false;
279
- try { verified = typeof p.birthCert === "string" && JSON.parse(p.birthCert).verified === true; } catch {}
280
- if (typeof p.ownerKeyHash === "string") {
281
- chain.push({
282
- domain,
283
- directiveId: "enrollSigner",
284
- actor: founder,
285
- payload: {
286
- keyHash: p.ownerKeyHash,
287
- grantedBy: founder,
288
- grantedAt: 1,
289
- ...(verified && apStr("issuer") ? { verifiedVia: apStr("issuer") } : {}),
290
- },
291
- });
292
- }
293
298
  if (apStr("issuer")) {
294
299
  chain.push({
295
300
  domain,
@@ -319,6 +324,7 @@ function lowerOne<P extends Record<string, unknown>, F extends Record<string, Fi
319
324
  const metadata: BusinessBirthMetadata = {
320
325
  aggregateId: decl.aggregate.id,
321
326
  domain,
327
+ owned: decl.aggregate.audience?.kind === "owned",
322
328
  publicPayloadSchema: publicSchema,
323
329
  rootId,
324
330
  namespace,
@@ -337,6 +343,9 @@ function lowerOne<P extends Record<string, unknown>, F extends Record<string, Fi
337
343
  const parent = {
338
344
  ...birthBase,
339
345
  businessBirth: metadata,
346
+ [BUSINESS_BIRTH_POLICY_BINDING]: (domains: readonly string[]) => {
347
+ decl.__nomosBirthPolicyDomains = domains;
348
+ },
340
349
  ...(applicationIntentOnlyBirths.has(decl) ? { businessBirthInternal: true } : {}),
341
350
  } as BusinessBirthDirective;
342
351
  return {
@@ -417,7 +426,11 @@ export function bindBusinessBirthPolicyDomains(
417
426
  domains: readonly string[],
418
427
  ): void {
419
428
  const declarations = businessBirthsOf(mod);
420
- if (declarations.length === 0) return;
429
+ const bindings = businessBirthPolicyBindingsOf(mod);
430
+ // Packages without a birth surface have no policy value to bind. In
431
+ // particular, an application-less package legitimately has no application
432
+ // birth-policy namespace inventory.
433
+ if (declarations.length === 0 && bindings.length === 0) return;
421
434
  const unique = [...new Set(domains)];
422
435
  if (unique.length === 0 || unique.some((value) => value.trim().length === 0)) {
423
436
  throw new Error("birth policy namespaces must be a non-empty list of domain names");
@@ -425,6 +438,9 @@ export function bindBusinessBirthPolicyDomains(
425
438
  for (const declaration of declarations) {
426
439
  declaration.__nomosBirthPolicyDomains = unique;
427
440
  }
441
+ for (const binding of bindings) {
442
+ binding[BUSINESS_BIRTH_POLICY_BINDING](unique);
443
+ }
428
444
  }
429
445
 
430
446
  /** Lower the one tenant-facing birth declaration into ordinary, gate-visible framework directives. */
@@ -434,13 +450,7 @@ export function expandBusinessBirthExports<M extends Record<string, unknown>>(
434
450
  birthPolicyDomains?: readonly string[],
435
451
  ): Record<string, unknown> {
436
452
  const declarations = businessBirthsOf(mod);
437
- if (declarations.length === 0) return mod;
438
- if (declarations.length > 1) {
439
- throw new Error(
440
- `domain '${domain}' declares ${declarations.length} business births; one generated birth(payload) cannot be ` +
441
- "ambiguous — model one born aggregate root per domain",
442
- );
443
- }
453
+ if (declarations.length === 0 && businessBirthPolicyBindingsOf(mod).length === 0) return mod;
444
454
  const compiledDomains = mod[BUSINESS_BIRTH_POLICY_DOMAINS_EXPORT];
445
455
  const selectedDomains = birthPolicyDomains ?? (
446
456
  Array.isArray(compiledDomains) && compiledDomains.every((value) => typeof value === "string")
@@ -449,9 +459,20 @@ export function expandBusinessBirthExports<M extends Record<string, unknown>>(
449
459
  );
450
460
  const policyDomains = [...new Set(selectedDomains)];
451
461
  if (policyDomains.length === 0 || policyDomains.some((value) => value.trim().length === 0)) {
452
- throw new Error(`birth(${declarations[0]!.aggregate.id}): compiled law declares no policy namespaces`);
462
+ const subject = declarations[0]?.aggregate.id ?? `domain '${domain}'`;
463
+ throw new Error(`birth(${subject}): compiled law declares no policy namespaces`);
453
464
  }
454
465
  bindBusinessBirthPolicyDomains(mod, policyDomains);
466
+ // A sliced parent intent may carry an already-lowered child birth through the
467
+ // typed binding above while exporting no raw BusinessBirthDecl of its own.
468
+ // Binding that closure is the complete job for this domain.
469
+ if (declarations.length === 0) return mod;
470
+ if (declarations.length > 1) {
471
+ throw new Error(
472
+ `domain '${domain}' declares ${declarations.length} business births; one generated birth(payload) cannot be ` +
473
+ "ambiguous — model one born aggregate root per domain",
474
+ );
475
+ }
455
476
  const lowered = lowerOne(declarations[0]!, domain, policyDomains);
456
477
  for (const [key, generated] of Object.entries(lowered)) {
457
478
  if (!Object.prototype.hasOwnProperty.call(mod, key)) continue;
@@ -29,12 +29,12 @@
29
29
  * generated, so they stay reproducible from the domain definitions.
30
30
  */
31
31
  import { z } from "zod";
32
- import { voMetaOf, collectPayloadValueObjectDescriptors, idOfMetaOf, idOfTargetKindOf, versionedRefOfMetaOf, brandedIdMetaOf, NOMOS_UNIT_BRAND } from "./value_object.js";
32
+ import { voMetaOf, collectPayloadValueObjectDescriptors, idOfMetaOf, idOfTargetKindOf, refOfMetaOf, versionedRefOfMetaOf, brandedIdMetaOf, NOMOS_UNIT_BRAND } from "./value_object.js";
33
33
  import { recursiveTypeBaseName, zodUnwrapLazy } from "./zod_lazy.js";
34
34
  import type { AggregateHandle } from "./aggregate.js";
35
35
  import type { WorkspaceInvariantDecl } from "./framework/workspace_invariant.js";
36
36
  import type { RelationTypeDecl } from "./relation_schema.js";
37
- import { isSha256DigestSchema, type Field, type FieldKind, type ValueObjectDescriptor, type ValueObjectFieldDescriptor } from "./fields.js";
37
+ import { isGitObjectIdSchema, isSha256DigestSchema, type Field, type FieldKind, type ValueObjectDescriptor, type ValueObjectFieldDescriptor } from "./fields.js";
38
38
  import { admitsNull, nullMetaOf, nullDocLine } from "./null_meta.js";
39
39
  import type { Directive } from "./directive.js";
40
40
  import { executeDirectiveToIntent } from "./wire_encode.js";
@@ -84,6 +84,7 @@ export type ToJsonWrap =
84
84
  | "voList" // a list of value objects — emit `.map((e) => e.toJson()).toList()`.
85
85
  | "wrapped" // a standalone branded-id or unit extension type — emit `.value`.
86
86
  | "ref" // an aggregate-specific NomosRef<T> — emit its `.id` wire scalar.
87
+ | "addressRef" // a workspace-qualified NomosRef<T> — preserve its complete typed address.
87
88
  | "versionedRef" // a publication pin + stable series — emit the atomic address pair.
88
89
  | "recipient"; // a framework-created Recipient<User> — emit its private delivery address.
89
90
 
@@ -426,7 +427,8 @@ export function collectPayloadIdOfTargets(directives: readonly { payloadSchema?:
426
427
  continue;
427
428
  }
428
429
  for (const zt of Object.values(shape)) {
429
- const target = idOfMetaOf(zt as z.ZodTypeAny);
430
+ const target = idOfMetaOf(zt as z.ZodTypeAny)
431
+ ?? refOfMetaOf(zt as z.ZodTypeAny)?.aggregateType;
430
432
  if (target !== undefined) out.add(target);
431
433
  }
432
434
  }
@@ -443,6 +445,18 @@ function introspectField(
443
445
  enumDartTypes: Map<string, string>,
444
446
  lazyNames?: Map<z.ZodTypeAny, string>,
445
447
  ): Omit<PayloadFieldSpec, "optional"> {
448
+ const addressed = refOfMetaOf(zt);
449
+ if (addressed !== undefined) {
450
+ const target = addressed.targetKind === "contract"
451
+ ? contractTypeName(addressed.aggregateType)
452
+ : concreteClassName(addressed.aggregateType);
453
+ return {
454
+ name,
455
+ dartType: `NomosRef<${target}>`,
456
+ referenceType: addressed.aggregateType,
457
+ toJson: "addressRef",
458
+ };
459
+ }
446
460
  const versioned = versionedRefOfMetaOf(zt);
447
461
  if (versioned !== undefined) {
448
462
  const target = versioned.targetKind === "contract"
@@ -518,6 +532,9 @@ function introspectField(
518
532
  if (isSha256DigestSchema(zt)) {
519
533
  return { name, dartType: "Sha256Digest", toJson: "wrapped", wrappedScalarKind: "string" };
520
534
  }
535
+ if (isGitObjectIdSchema(zt)) {
536
+ return { name, dartType: "GitObjectId", toJson: "wrapped", wrappedScalarKind: "string" };
537
+ }
521
538
  // brandedId("SiteId"): a standalone nominal string → the generated extension type.
522
539
  const brand = brandedIdMetaOf(zt);
523
540
  if (brand !== undefined) {
@@ -625,7 +642,7 @@ interface AggFieldSpec {
625
642
  kind: MaterializedFieldKind;
626
643
  dartType: string;
627
644
  /** A canonical semantic string wrapper whose wire representation remains String. */
628
- semanticString?: "sha256";
645
+ semanticString?: "sha256" | "git-object-id";
629
646
  enumValues?: string[];
630
647
  /** For an own-id string field represented as `NomosRef<TAggregate>`. */
631
648
  valueClass?: string;
@@ -845,6 +862,9 @@ function readKeyParamSpec(
845
862
  if (fieldSpec.semanticString === "sha256") {
846
863
  return { dartType: "Sha256Digest", valueExpr: `${paramName}.value` };
847
864
  }
865
+ if (fieldSpec.semanticString === "git-object-id") {
866
+ return { dartType: "GitObjectId", valueExpr: `${paramName}.value` };
867
+ }
848
868
  return { dartType: "String", valueExpr: paramName };
849
869
  }
850
870
 
@@ -954,12 +974,19 @@ function aggFieldSpec(
954
974
  wireName,
955
975
  kind: field.kind,
956
976
  dartType: valueClass === undefined
957
- ? isSha256DigestSchema(field.zod as z.ZodTypeAny) ? "Sha256Digest" : "String"
977
+ ? isSha256DigestSchema(field.zod as z.ZodTypeAny)
978
+ ? "Sha256Digest"
979
+ : isGitObjectIdSchema(field.zod as z.ZodTypeAny)
980
+ ? "GitObjectId"
981
+ : "String"
958
982
  : `NomosRef<${valueClass}>`,
959
983
  optional,
960
984
  ...(valueClass === undefined && isSha256DigestSchema(field.zod as z.ZodTypeAny)
961
985
  ? { semanticString: "sha256" as const }
962
986
  : {}),
987
+ ...(valueClass === undefined && isGitObjectIdSchema(field.zod as z.ZodTypeAny)
988
+ ? { semanticString: "git-object-id" as const }
989
+ : {}),
963
990
  ...(valueClass !== undefined && valueAggregateType !== undefined
964
991
  ? { valueClass, valueAggregateType }
965
992
  : {}),
@@ -2610,19 +2637,29 @@ export function generateDartActivityRegistry(entries: readonly PackageActivityEn
2610
2637
  ` const NomosModelReview({required this.checkedActions, required this.candidateActions, required this.sameOutcome, required this.differences});`,
2611
2638
  `}`,
2612
2639
  ``,
2613
- `enum NomosModelAdoptionStatus { adopted, needsDecision, blocked }`,
2640
+ `enum NomosModelAdoptionStatus { adopted, needsStaging, needsDecision, blocked }`,
2614
2641
  ``,
2615
2642
  `class NomosModelAdoption {`,
2616
2643
  ` final NomosModelAdoptionStatus status;`,
2617
2644
  ` final String summary;`,
2618
2645
  ` final NomosModelReview review;`,
2646
+ ` final Future<NomosModelAdoption> Function()? _stage;`,
2619
2647
  ` final Future<NomosModelAdoption> Function(String)? _accept;`,
2620
2648
  ` const NomosModelAdoption.adopted({required this.summary, required this.review})`,
2621
- ` : status = NomosModelAdoptionStatus.adopted, _accept = null;`,
2649
+ ` : status = NomosModelAdoptionStatus.adopted, _stage = null, _accept = null;`,
2622
2650
  ` const NomosModelAdoption.blocked({required this.summary, required this.review})`,
2623
- ` : status = NomosModelAdoptionStatus.blocked, _accept = null;`,
2651
+ ` : status = NomosModelAdoptionStatus.blocked, _stage = null, _accept = null;`,
2652
+ ` NomosModelAdoption.needsStaging({required this.summary, required this.review, required Future<NomosModelAdoption> Function() stage})`,
2653
+ ` : status = NomosModelAdoptionStatus.needsStaging, _stage = stage, _accept = null;`,
2624
2654
  ` NomosModelAdoption.needsDecision({required this.summary, required this.review, required Future<NomosModelAdoption> Function(String) accept})`,
2625
- ` : status = NomosModelAdoptionStatus.needsDecision, _accept = accept;`,
2655
+ ` : status = NomosModelAdoptionStatus.needsDecision, _stage = null, _accept = accept;`,
2656
+ ` Future<NomosModelAdoption> stage() {`,
2657
+ ` final stageCandidate = _stage;`,
2658
+ ` if (status != NomosModelAdoptionStatus.needsStaging || stageCandidate == null) {`,
2659
+ ` throw StateError('This model change is not waiting to be staged.');`,
2660
+ ` }`,
2661
+ ` return stageCandidate();`,
2662
+ ` }`,
2626
2663
  ` Future<NomosModelAdoption> accept(String because) {`,
2627
2664
  ` final reason = because.trim();`,
2628
2665
  ` final acceptDecision = _accept;`,
@@ -2974,6 +3011,11 @@ function parseField(s: AggFieldSpec, aggregate: string): string {
2974
3011
  ? `() { final v = ${reader}; return v == null ? null : Sha256Digest(v); }()`
2975
3012
  : `Sha256Digest(${reader})`;
2976
3013
  }
3014
+ if (s.semanticString === "git-object-id") {
3015
+ return opt
3016
+ ? `() { final v = ${reader}; return v == null ? null : GitObjectId(v); }()`
3017
+ : `GitObjectId(${reader})`;
3018
+ }
2977
3019
  return reader;
2978
3020
  }
2979
3021
  case "json":
@@ -3060,7 +3102,9 @@ function slotDecodeCallback(s: AggFieldSpec, mode: "aggregate" | "readModel"): s
3060
3102
  }
3061
3103
  return s.semanticString === "sha256"
3062
3104
  ? `(v) => Sha256Digest(v as String)`
3063
- : `(v) => v as String`;
3105
+ : s.semanticString === "git-object-id"
3106
+ ? `(v) => GitObjectId(v as String)`
3107
+ : `(v) => v as String`;
3064
3108
  case "json":
3065
3109
  if (s.voClass !== undefined) {
3066
3110
  return `(v) => ${s.voClass}.fromProjected((v as Map).cast<String, Object?>())`;
@@ -3208,6 +3252,11 @@ function parseReadModelField(s: AggFieldSpec, aggregate: string): string {
3208
3252
  ? `() { final v = ${reader}; return v == null ? null : Sha256Digest(v); }()`
3209
3253
  : `Sha256Digest(${reader})`;
3210
3254
  }
3255
+ if (s.semanticString === "git-object-id") {
3256
+ return opt
3257
+ ? `() { final v = ${reader}; return v == null ? null : GitObjectId(v); }()`
3258
+ : `GitObjectId(${reader})`;
3259
+ }
3211
3260
  return reader;
3212
3261
  }
3213
3262
  case "json":
@@ -3630,6 +3679,8 @@ export function toJsonExpr(spec: PayloadFieldSpec, accessor: string): string {
3630
3679
  return `${accessor}.value`;
3631
3680
  case "ref":
3632
3681
  return `NmsInternalNomosRefs.id(${accessor})`;
3682
+ case "addressRef":
3683
+ return `NmsInternalNomosRefs.toJson(${accessor})`;
3633
3684
  case "versionedRef":
3634
3685
  return `{'pinned': NmsInternalNomosRefs.toJson(${accessor}.pinned), 'series': NmsInternalNomosRefs.toJson(${accessor}.series)}`;
3635
3686
  case "recipient":
@@ -4309,7 +4360,8 @@ function buildProbePayload(
4309
4360
  zt = zodInnerType(zt);
4310
4361
  }
4311
4362
  const sentinel = fieldBySentinel.get(name);
4312
- out[name] = sentinel !== undefined && zodTypeName(zt) === "ZodString" && !isSha256DigestSchema(zt)
4363
+ out[name] = sentinel !== undefined && zodTypeName(zt) === "ZodString" &&
4364
+ !isSha256DigestSchema(zt) && !isGitObjectIdSchema(zt)
4313
4365
  ? sentinel
4314
4366
  : sampleFromZod(zt);
4315
4367
  }
@@ -4324,7 +4376,11 @@ function sampleFromZod(zt: z.ZodTypeAny): unknown {
4324
4376
  }
4325
4377
  switch (zodTypeName(t)) {
4326
4378
  case "ZodString":
4327
- return isSha256DigestSchema(t) ? "0".repeat(64) : "x";
4379
+ return isSha256DigestSchema(t)
4380
+ ? "0".repeat(64)
4381
+ : isGitObjectIdSchema(t)
4382
+ ? "0".repeat(40)
4383
+ : "x";
4328
4384
  case "ZodNumber":
4329
4385
  return 1;
4330
4386
  case "ZodBoolean":