@githolon/dsl 0.114.0 → 0.116.2

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 (86) hide show
  1. package/assets/operation-contract.wasm +0 -0
  2. package/dart/CHANGELOG.md +55 -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/nms_generated_runtime.dart +1 -0
  8. package/dart/lib/src/operation_receipts.dart +74 -7
  9. package/dart/lib/src/reception/dart_reception.dart +26 -6
  10. package/dart/lib/src/result_usda.dart +64 -27
  11. package/dart/lib/src/runner/node-entry.mjs +4733 -1534
  12. package/dart/lib/src/runtime_v2_transport.dart +3 -0
  13. package/dart/lib/src/sync_status_projection.dart +11 -0
  14. package/dart/pubspec.lock +1 -1
  15. package/dart/pubspec.yaml +2 -2
  16. package/dart/test/console_enrolment_test.dart +47 -0
  17. package/dart/test/kernel_refused_follow_test.dart +36 -0
  18. package/dart/test/model_adoption_test.dart +49 -0
  19. package/dart/test/result_usda_test.dart +67 -0
  20. package/dart/test/runtime_projections_test.dart +41 -0
  21. package/dart_types/CHANGELOG.md +30 -0
  22. package/dart_types/lib/src/approved_model.dart +78 -1
  23. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +528 -2
  24. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +24 -0
  25. package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pb.dart +1897 -112
  26. package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pb.dart +98 -0
  27. package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pbenum.dart +11 -1
  28. package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pb.dart +16 -0
  29. package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pb.dart +43 -2
  30. package/dart_types/lib/src/vo_canonical.dart +185 -0
  31. package/dart_types/pubspec.lock +1 -1
  32. package/dart_types/pubspec.yaml +3 -1
  33. package/dart_types/test/vo_canonical_test.dart +91 -0
  34. package/package.json +4 -2
  35. package/src/aggregate.ts +95 -0
  36. package/src/anonymous_value_interning.ts +205 -0
  37. package/src/baseline.ts +27 -0
  38. package/src/baseline_export.ts +432 -0
  39. package/src/baseline_import.ts +312 -0
  40. package/src/baseline_usda.ts +185 -0
  41. package/src/birth_key_rule.ts +472 -0
  42. package/src/births.ts +207 -24
  43. package/src/build_package.ts +47 -7
  44. package/src/build_provenance.ts +10 -0
  45. package/src/business_birth.ts +50 -4
  46. package/src/check_package_main.ts +50 -6
  47. package/src/codegen_call_proto.ts +5 -18
  48. package/src/codegen_call_usda.ts +6 -3
  49. package/src/codegen_dart.ts +114 -60
  50. package/src/codegen_dart_client.ts +52 -6
  51. package/src/codegen_dart_facade.ts +57 -35
  52. package/src/codegen_dart_framework_calls.ts +20 -1
  53. package/src/codegen_proof.ts +5 -2
  54. package/src/codegen_result_proto.ts +8 -6
  55. package/src/codegen_result_usda.ts +12 -4
  56. package/src/codegen_ts.ts +109 -10
  57. package/src/compatibility_graph.ts +41 -4
  58. package/src/compatibility_usda.ts +11 -1
  59. package/src/compile_package_main.ts +222 -52
  60. package/src/composed_wire_identity.ts +24 -0
  61. package/src/directive.ts +6 -0
  62. package/src/domain_requirements.ts +75 -0
  63. package/src/engine_entry.ts +12 -4
  64. package/src/engine_javascript.ts +12 -6
  65. package/src/fields.ts +109 -0
  66. package/src/identity_usda.ts +80 -103
  67. package/src/index.ts +35 -1
  68. package/src/intent.ts +33 -3
  69. package/src/law_check.ts +34 -0
  70. package/src/law_graph.ts +142 -1
  71. package/src/law_graph_usda.ts +27 -144
  72. package/src/law_layer_prims.ts +174 -0
  73. package/src/law_unit_selection.ts +45 -0
  74. package/src/law_units.ts +7 -6
  75. package/src/law_version.ts +108 -0
  76. package/src/package_config.ts +44 -0
  77. package/src/package_dependencies.ts +8 -0
  78. package/src/point_guard.ts +5 -1
  79. package/src/release_inspection.ts +6 -4
  80. package/src/release_publication.ts +6 -5
  81. package/src/usda_text.ts +78 -3
  82. package/src/value_identity.ts +355 -0
  83. package/src/value_object.ts +35 -4
  84. package/src/wire.ts +2 -0
  85. package/src/wire_paths.ts +17 -0
  86. package/src/workspace_routing.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@githolon/dsl",
3
- "version": "0.114.0",
3
+ "version": "0.116.2",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "Nomos 2 domain-authoring DSL: aggregates + directives in TS, executed and encoded to the Rust kernel's wire shapes.",
@@ -27,6 +27,8 @@
27
27
  },
28
28
  "./release-publication": "./src/release_publication.ts",
29
29
  "./release-inspection": "./src/release_inspection.ts",
30
+ "./law-version": "./src/law_version.ts",
31
+ "./baseline": "./src/baseline.ts",
30
32
  "./workspace-type": "./src/workspace_type.ts",
31
33
  "./workspace-routing": "./src/workspace_routing.ts",
32
34
  "./workspace-sharding": "./src/workspace_sharding.ts",
@@ -75,7 +77,7 @@
75
77
  "test": "vitest run"
76
78
  },
77
79
  "dependencies": {
78
- "@githolon/runtime": "^0.114.0",
80
+ "@githolon/runtime": "^0.116.2",
79
81
  "@bufbuild/protobuf": "^2.13.0",
80
82
  "@bufbuild/protoc-gen-es": "^2.13.0",
81
83
  "esbuild": "0.24.2",
package/src/aggregate.ts CHANGED
@@ -18,6 +18,7 @@ import { admitsNull, attributionField, t } from "./fields.js";
18
18
  import { satisfiesContract, type ContractHandle } from "./contract.js";
19
19
  import type { FulfilmentDecl } from "./fulfilment.js";
20
20
  import { Lww } from "./drivers.js";
21
+ import { canonicalJsonValue } from "./value_identity.js";
21
22
 
22
23
  /**
23
24
  * A pure, replay-stable predicate over one aggregate's PROJECTED snapshot —
@@ -42,6 +43,90 @@ export type AggregateInvariantFn = (
42
43
  previous?: Readonly<Record<string, unknown>> | null,
43
44
  ) => AggregateInvariantVerdict;
44
45
 
46
+ /**
47
+ * BIRTH KEYS ARE WRITE-ONCE (CAP-804 / CO2-1687). A field-keyed `birth(Aggregate, { key })` names the born
48
+ * workspace `workspaceNameForKey(namespace, <key value>)` — fixed at birth, forever. So the framework, not
49
+ * the tenant, attaches a write-once rule to that field of the born aggregate: it rides the aggregate's
50
+ * composed invariant (the SAME #41 gate machinery as `.fromBirth()` presence and CO2's hand-written
51
+ * `immutableIdentity`), so it is judged at the kernel on author, on edge admission and on verify.
52
+ *
53
+ * - creation (`previous === null`), or a field never set before → ADMITTED (the birth seeds it);
54
+ * - a write that leaves the value unchanged (a genesis seed re-stating the key) → ADMITTED;
55
+ * - a write that CHANGES the value → REFUSED (`{ refuse }`: no strike, no state change) with the typed
56
+ * code `<Aggregate>.<field>:birth-key-write-once` (see {@link BIRTH_KEY_WRITE_ONCE_CODE}).
57
+ *
58
+ * A caller that supplies no transition context (`previous === undefined`, a snapshot-only legacy oracle)
59
+ * cannot show a change, so the rule holds vacuously there — it only ever judges transitions.
60
+ *
61
+ * ERA: the rule lives in the invariant BODY, which ships in the law's own engine bundle; invariant
62
+ * PRESENCE (the hashed manifest bit) is unchanged, because a birth key is always a `.fromBirth()` field
63
+ * and every `.fromBirth()` aggregate already declares the composed presence invariant. Chains authored
64
+ * under earlier law replay under that law byte-identically; only law compiled with this DSL carries it.
65
+ *
66
+ * VALUE-FREE: the refusal names the field, never a value, and it can only tell a writer whether its
67
+ * write would change the key — which the writer, already inside the workspace, can derive from the
68
+ * workspace's own name. It therefore keeps the presence check's `invariantValueFree` posture.
69
+ */
70
+ export const BIRTH_KEY_WRITE_ONCE_CODE = "birth-key-write-once";
71
+
72
+ /** @internal Where `birth()` registers the write-once birth-key fields of an aggregate. Symbol-keyed so it
73
+ * is copied by the handle builders' spreads (every `.public()`/`.readableBy()`/`.owned()` handle shares
74
+ * ONE set) yet invisible to `Object.keys`/`JSON.stringify` (never hashed, never emitted). */
75
+ export const BIRTH_KEY_FIELDS: unique symbol = Symbol.for("nomos.birthKeyWriteOnceFields");
76
+
77
+ /** True for an aggregate-invariant refusal code produced by the birth-key write-once rule. */
78
+ export function isBirthKeyWriteOnceRefusal(code: string): boolean {
79
+ return /^[^\s]+\.[^\s.:]+:birth-key-write-once(?:\s|$)/.test(code);
80
+ }
81
+
82
+ /** @internal Register `field` of `aggregate` as a write-once birth key (called by `birth()`). */
83
+ export function __declareBirthKeyWriteOnce(aggregate: AggregateHandle, field: string): void {
84
+ const registry = (aggregate as unknown as { [BIRTH_KEY_FIELDS]?: Set<string> })[BIRTH_KEY_FIELDS];
85
+ if (registry === undefined) {
86
+ throw new Error(
87
+ `birth(${aggregate.id}): this aggregate handle cannot carry the framework's write-once birth-key rule ` +
88
+ `(it was not built by aggregate())`,
89
+ );
90
+ }
91
+ registry.add(field);
92
+ }
93
+
94
+ /** The write-once birth-key fields `birth()` registered on this aggregate (empty when none). */
95
+ export function birthKeyWriteOnceFields(aggregate: AggregateHandle): readonly string[] {
96
+ const registry = (aggregate as unknown as { [BIRTH_KEY_FIELDS]?: Set<string> })[BIRTH_KEY_FIELDS];
97
+ return registry === undefined ? [] : [...registry].sort();
98
+ }
99
+
100
+ function sameKeyValue(a: unknown, b: unknown): boolean {
101
+ if (a === b) return true;
102
+ if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
103
+ // Value equality (RFC 8785), not text equality: member order is not part of a key's value (CAP-853).
104
+ return canonicalJsonValue(a) === canonicalJsonValue(b);
105
+ }
106
+
107
+ /** @internal The write-once judgement over one transition (exported for direct tests). */
108
+ export function judgeBirthKeyWriteOnce(
109
+ aggregateId: string,
110
+ fields: Iterable<string>,
111
+ snapshot: Readonly<Record<string, unknown>>,
112
+ previous: Readonly<Record<string, unknown>> | null | undefined,
113
+ ): AggregateInvariantVerdict {
114
+ if (previous === undefined || previous === null) return { accept: true };
115
+ for (const name of fields) {
116
+ const before = previous[name];
117
+ if (before === undefined || before === null) continue;
118
+ if (!sameKeyValue(before, snapshot[name])) {
119
+ return {
120
+ refuse:
121
+ `${aggregateId}.${name}:${BIRTH_KEY_WRITE_ONCE_CODE} — '${name}' is the birth key of ${aggregateId}: the ` +
122
+ `born workspace's name was derived from it at birth and is fixed forever, so it may be seeded with the ` +
123
+ `same value but never changed (architecture/choosing_a_birth_key.md)`,
124
+ };
125
+ }
126
+ }
127
+ return { accept: true };
128
+ }
129
+
45
130
  /**
46
131
  * READ VISIBILITY (the native-relations read gate) — the DECLARED posture of an
47
132
  * aggregate's data on the read side. `public` (the DEFAULT, and the only value
@@ -640,6 +725,10 @@ export function aggregate<const Id extends string, F extends Record<string, Fiel
640
725
  return userInvariant !== undefined ? userInvariant(snapshot, previous) : { accept: true };
641
726
  }
642
727
  : userInvariant;
728
+ // BIRTH-KEY WRITE-ONCE (CAP-804): `birth()` registers its key field here AFTER this aggregate is
729
+ // declared; the composed invariant reads the shared set at evaluation time. A birth key is always a
730
+ // `.fromBirth()` field, so the presence invariant (and its hashed presence bit) already exists.
731
+ const birthKeyFields = new Set<string>();
643
732
  const composedInvariant: AggregateInvariantFn | undefined =
644
733
  fromBirthFields.length > 0
645
734
  ? (snapshot, previous) => {
@@ -649,6 +738,10 @@ export function aggregate<const Id extends string, F extends Record<string, Fiel
649
738
  return { reject: `${id}.${name} is fromBirth() — every ${id} must carry it from creation (the plan left it unset)` };
650
739
  }
651
740
  }
741
+ if (birthKeyFields.size > 0) {
742
+ const writeOnce = judgeBirthKeyWriteOnce(id, birthKeyFields, snapshot, previous);
743
+ if (!("accept" in writeOnce)) return writeOnce;
744
+ }
652
745
  return afterFulfilment !== undefined ? afterFulfilment(snapshot, previous) : { accept: true };
653
746
  }
654
747
  : afterFulfilment;
@@ -660,6 +753,8 @@ export function aggregate<const Id extends string, F extends Record<string, Fiel
660
753
  fields: stored as StoredFields<F>,
661
754
  hasMany: hasMany as HasManyFields<F>,
662
755
  __isAggregateHandle: true as const,
756
+ // Only a `.fromBirth()` aggregate can be born, so only it carries the registry (see BIRTH_KEY_FIELDS).
757
+ ...(fromBirthFields.length > 0 ? { [BIRTH_KEY_FIELDS]: birthKeyFields } : {}),
663
758
  ...(opts?.fulfils !== undefined ? { fulfilment: opts.fulfils } : {}),
664
759
  ...(composedInvariant !== undefined
665
760
  ? { hasInvariant: true as const, invariant: composedInvariant }
@@ -0,0 +1,205 @@
1
+ // NOMOS — Structural interning of anonymous value types: one semantic shape, one canonical node.
2
+ //
3
+ // Anonymous value types are structural values, not independently lived declarations. The graph
4
+ // deliberately retains their source owner for diagnostics, but USDA must not turn that provenance
5
+ // into thousands of duplicate runtime prims. This computes a semantic key which excludes owner
6
+ // identity, then points every equivalent type graph — and each of its member fields and union
7
+ // variants — at one canonical node. Declared types remain untouched. Recursive graphs keep their
8
+ // owner-scoped identity for now: aliasing a cycle needs an explicit recursion-isomorphism proof,
9
+ // whereas every acyclic case below is exact by construction.
10
+ //
11
+ // ONE interning, TWO consumers. The USDA emitter (law_graph_usda.ts) redirects duplicate prims to
12
+ // the canonical prim; the graph discovery (law_graph.ts) ledgers a nested payload field's wire key
13
+ // against the canonical member, because that is the key installed law serves and generated clients
14
+ // encode (codegen_call_proto.ts `composedWireIdentity`). Computing it twice from the same nodes
15
+ // is what keeps the ledger, the law and the clients on one allocation (CAP-660).
16
+ import type {
17
+ LawAnonymousValueFieldNode,
18
+ LawAnonymousValueTypeNode,
19
+ LawAnonymousValueVariantNode,
20
+ } from "./law_graph.js";
21
+
22
+ export interface AnonymousValueInterningInput {
23
+ readonly types: readonly LawAnonymousValueTypeNode[];
24
+ readonly fields: readonly LawAnonymousValueFieldNode[];
25
+ readonly variants: readonly LawAnonymousValueVariantNode[];
26
+ /** Whether this type is emitted as its own `Types/t_<sid>` prim (wrappers and framework scalars are not). */
27
+ readonly ownsPrim: (type: LawAnonymousValueTypeNode) => boolean;
28
+ }
29
+
30
+ export interface AnonymousValueInterning {
31
+ /** Every prim-owning type with a semantic key → its canonical type id (itself when it is the canonical one). */
32
+ readonly canonicalTypeIdById: ReadonlyMap<string, string>;
33
+ /** A duplicate type's member field → the canonical type's equivalent member (duplicates only). */
34
+ readonly canonicalFieldIdById: ReadonlyMap<string, string>;
35
+ /** A duplicate sum's variant → the canonical sum's equivalent variant (duplicates only). */
36
+ readonly canonicalVariantIdById: ReadonlyMap<string, string>;
37
+ /** The owner-free structural key of a type, or undefined for a recursive (or unknown) graph. */
38
+ readonly semanticTypeKey: (id: string) => string | undefined;
39
+ }
40
+
41
+ /** A type whose shape is spelled by the framework's own scalar prim, or a wrapper, owns no prim. */
42
+ export function anonymousTypeOwnsPrim(type: LawAnonymousValueTypeNode): boolean {
43
+ const expression = type.expression;
44
+ if (expression.kind === "string" && expression.constraints.length === 0) return false;
45
+ if (expression.kind === "integer" && expression.constraints.length === 0) return false;
46
+ if (expression.kind === "boolean") return false;
47
+ if (expression.kind === "nullable" || expression.kind === "default") return false;
48
+ return true;
49
+ }
50
+
51
+ export function internAnonymousValues(input: AnonymousValueInterningInput): AnonymousValueInterning {
52
+ const typeById = new Map<string, LawAnonymousValueTypeNode>(input.types.map((node) => [node.entity.id as string, node] as const));
53
+ const fieldsByOwner = new Map<string, LawAnonymousValueFieldNode[]>();
54
+ for (const field of input.fields) {
55
+ const owned = fieldsByOwner.get(field.owner.id) ?? [];
56
+ owned.push(field);
57
+ fieldsByOwner.set(field.owner.id, owned);
58
+ }
59
+ const variantsByOwner = new Map<string, LawAnonymousValueVariantNode[]>();
60
+ for (const variant of input.variants) {
61
+ const owned = variantsByOwner.get(variant.owner.id) ?? [];
62
+ owned.push(variant);
63
+ variantsByOwner.set(variant.owner.id, owned);
64
+ }
65
+
66
+ const memo = new Map<string, string | null>();
67
+ const semanticTypeKey = (id: string, active = new Set<string>()): string | undefined => {
68
+ const memoized = memo.get(id);
69
+ if (memoized !== undefined) return memoized ?? undefined;
70
+ if (active.has(id)) {
71
+ memo.set(id, null);
72
+ return undefined;
73
+ }
74
+ const node = typeById.get(id);
75
+ if (node === undefined) return undefined;
76
+ active.add(id);
77
+ const childKey = (childId: string): string | undefined => semanticTypeKey(childId, active);
78
+ let key: string | undefined;
79
+ switch (node.expression.kind) {
80
+ case "string":
81
+ case "number":
82
+ case "integer":
83
+ case "boolean":
84
+ case "null":
85
+ case "unknown":
86
+ case "enum":
87
+ case "literal":
88
+ key = JSON.stringify(node.expression);
89
+ break;
90
+ case "list": {
91
+ const child = childKey(node.expression.element.id);
92
+ if (child !== undefined) key = JSON.stringify(["list", child]);
93
+ break;
94
+ }
95
+ case "map": {
96
+ const child = childKey(node.expression.value.id);
97
+ if (child !== undefined) key = JSON.stringify(["map", child]);
98
+ break;
99
+ }
100
+ case "nullable":
101
+ case "default": {
102
+ // Wrappers do not own prims, but their meaning must survive structural
103
+ // interning. A nullable field is not the same contract as a non-null field.
104
+ const child = childKey(node.expression.value.id);
105
+ if (child !== undefined) key = JSON.stringify(node.expression.kind === "nullable"
106
+ ? ["nullable", child]
107
+ : ["default", child, node.expression.defaultValue]);
108
+ break;
109
+ }
110
+ case "record": {
111
+ const members: Array<readonly [string, boolean, string]> = [];
112
+ for (const field of fieldsByOwner.get(id) ?? []) {
113
+ const child = childKey(field.value.id);
114
+ if (child === undefined) break;
115
+ members.push([field.name, field.required, child]);
116
+ }
117
+ if (members.length === (fieldsByOwner.get(id)?.length ?? 0)) {
118
+ members.sort(([left], [right]) => left.localeCompare(right));
119
+ key = JSON.stringify(["record", members,
120
+ node.expression.semantic?.kind === "value-object" ? node.expression.semantic.geometry : undefined]);
121
+ }
122
+ break;
123
+ }
124
+ case "sum": {
125
+ const members: Array<readonly [string, string]> = [];
126
+ for (const variant of variantsByOwner.get(id) ?? []) {
127
+ const child = childKey(variant.value.id);
128
+ if (child === undefined) break;
129
+ // A union arm's discriminator is part of its wire contract. Two sums
130
+ // with the same carried value shapes but different arm labels cannot
131
+ // share one USDA prim: authoring reconstructs the nominal oneof from
132
+ // these labels before it decodes the generated Protobuf call.
133
+ members.push([variant.label ?? variant.entity.label, child]);
134
+ }
135
+ if (members.length === (variantsByOwner.get(id)?.length ?? 0)) {
136
+ members.sort(([leftLabel, leftChild], [rightLabel, rightChild]) =>
137
+ leftLabel.localeCompare(rightLabel) || leftChild.localeCompare(rightChild));
138
+ key = JSON.stringify(["sum", members]);
139
+ }
140
+ break;
141
+ }
142
+ case "recursive":
143
+ key = undefined;
144
+ break;
145
+ }
146
+ active.delete(id);
147
+ memo.set(id, key ?? null);
148
+ return key;
149
+ };
150
+
151
+ const canonicalTypeBySemanticKey = new Map<string, LawAnonymousValueTypeNode>();
152
+ const canonicalTypeIdById = new Map<string, string>();
153
+ for (const type of [...input.types].sort((left, right) => left.entity.sid.localeCompare(right.entity.sid))) {
154
+ if (!input.ownsPrim(type)) continue;
155
+ const key = semanticTypeKey(type.entity.id);
156
+ if (key === undefined) continue;
157
+ const canonical = canonicalTypeBySemanticKey.get(key);
158
+ if (canonical === undefined) {
159
+ canonicalTypeBySemanticKey.set(key, type);
160
+ canonicalTypeIdById.set(type.entity.id, type.entity.id);
161
+ continue;
162
+ }
163
+ canonicalTypeIdById.set(type.entity.id, canonical.entity.id);
164
+ }
165
+
166
+ // When two record/sum types share one canonical prim, their nested structural references must
167
+ // share the canonical child paths too. This preserves effects which target a nested payload field;
168
+ // no dangling path is allowed merely because its source owner was the non-canonical occurrence.
169
+ const canonicalFieldIdById = new Map<string, string>();
170
+ const canonicalVariantIdById = new Map<string, string>();
171
+ for (const [duplicateId, canonicalId] of canonicalTypeIdById) {
172
+ if (duplicateId === canonicalId) continue;
173
+ const canonicalFields = new Map<string, LawAnonymousValueFieldNode>();
174
+ for (const field of fieldsByOwner.get(canonicalId) ?? []) {
175
+ const child = semanticTypeKey(field.value.id);
176
+ if (child !== undefined) canonicalFields.set(JSON.stringify([field.name, field.required, child]), field);
177
+ }
178
+ for (const field of fieldsByOwner.get(duplicateId) ?? []) {
179
+ const child = semanticTypeKey(field.value.id);
180
+ const canonical = child === undefined
181
+ ? undefined
182
+ : canonicalFields.get(JSON.stringify([field.name, field.required, child]));
183
+ if (canonical !== undefined) canonicalFieldIdById.set(field.entity.id, canonical.entity.id);
184
+ }
185
+
186
+ const canonicalVariants = new Map<string, LawAnonymousValueVariantNode[]>();
187
+ for (const variant of variantsByOwner.get(canonicalId) ?? []) {
188
+ const child = semanticTypeKey(variant.value.id);
189
+ if (child === undefined) continue;
190
+ const key = JSON.stringify([variant.label ?? variant.entity.label, child]);
191
+ const matches = canonicalVariants.get(key) ?? [];
192
+ matches.push(variant);
193
+ canonicalVariants.set(key, matches);
194
+ }
195
+ for (const variant of variantsByOwner.get(duplicateId) ?? []) {
196
+ const child = semanticTypeKey(variant.value.id);
197
+ if (child === undefined) continue;
198
+ const key = JSON.stringify([variant.label ?? variant.entity.label, child]);
199
+ const canonical = canonicalVariants.get(key)?.shift();
200
+ if (canonical !== undefined) canonicalVariantIdById.set(variant.entity.id, canonical.entity.id);
201
+ }
202
+ }
203
+
204
+ return { canonicalTypeIdById, canonicalFieldIdById, canonicalVariantIdById, semanticTypeKey: (id) => semanticTypeKey(id) };
205
+ }
@@ -0,0 +1,27 @@
1
+ // NOMOS — The released-baseline surface `githolon baseline` and CI load (`@githolon/dsl/baseline`).
2
+ //
3
+ // One import for the three halves of CAP-660: the artifact (baseline_usda.ts), its export from
4
+ // verified units (baseline_export.ts) and its verification/import against a ledger (baseline_import.ts).
5
+ export {
6
+ BASELINE_NO_RELEASE,
7
+ NOMOS_BASELINE_FILENAME,
8
+ NOMOS_BASELINE_FORMAT,
9
+ baselineSha256,
10
+ isBaselineUsda,
11
+ parseBaselineUsda,
12
+ serializeBaselineUsda,
13
+ type BaselineSource,
14
+ type BaselineWireAllocation,
15
+ type ReleasedBaseline,
16
+ } from "./baseline_usda.js";
17
+ export { exportBaselineFromUnits, type BaselineExport, type BaselineExportInput } from "./baseline_export.js";
18
+ export {
19
+ BaselineRefusal,
20
+ assertBaselineRetained,
21
+ importBaseline,
22
+ verifyBaselinePin,
23
+ type BaselineImport,
24
+ type BaselinePin,
25
+ type BaselineRefusalCode,
26
+ } from "./baseline_import.js";
27
+ export { lawUnitDirectoryName } from "./law_units.js";