@githolon/dsl 0.13.0 → 0.15.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.
package/src/usd_layers.ts CHANGED
@@ -143,6 +143,40 @@ function sortedSchema(schema: WireSchema): WireSchema {
143
143
  return out;
144
144
  }
145
145
 
146
+ function domainLayerAttrLines(domain: string, layer: UsdLayer, indent: string): string[] {
147
+ return [
148
+ `${indent}custom string nomos:kind = "domain"`,
149
+ `${indent}custom string nomos:domain = ${usdaString(domain)}`,
150
+ ...(layer.nomosIdentityManifest !== undefined
151
+ ? [`${indent}custom string nomos:identityManifestHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosIdentityManifest)))}`]
152
+ : []),
153
+ ...(layer.nomosReadProjection !== undefined
154
+ ? [`${indent}custom string nomos:readProjectionHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosReadProjection)))}`]
155
+ : []),
156
+ ...(layer.nomosStableIds !== undefined
157
+ ? [`${indent}custom string nomos:stableIdsHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosStableIds)))}`]
158
+ : []),
159
+ ...(layer.nomosReadGate !== undefined
160
+ ? [`${indent}custom string nomos:readGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosReadGate)))}`]
161
+ : []),
162
+ ...(layer.nomosReadVisibility !== undefined
163
+ ? [`${indent}custom string nomos:readVisibilityHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosReadVisibility)))}`]
164
+ : []),
165
+ ...(layer.nomosEncryption !== undefined
166
+ ? [`${indent}custom string nomos:encryptionHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosEncryption)))}`]
167
+ : []),
168
+ ...(layer.nomosRelations !== undefined
169
+ ? [`${indent}custom string nomos:relationsHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosRelations)))}`]
170
+ : []),
171
+ ...(layer.nomosRelationGate !== undefined
172
+ ? [`${indent}custom string nomos:relationGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosRelationGate)))}`]
173
+ : []),
174
+ ...(layer.nomosAttestationGate !== undefined
175
+ ? [`${indent}custom string nomos:attestationGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosAttestationGate)))}`]
176
+ : []),
177
+ ];
178
+ }
179
+
146
180
  // ── per-module layer emission ────────────────────────────────────────────────────────
147
181
 
148
182
  /** One authored module of a domain's ordered module list (weak → strong). */
@@ -349,31 +383,9 @@ export function usdaLayerText(
349
383
  `{`,
350
384
  ` def Scope ${usdaString(primName(domain))}`,
351
385
  ` {`,
352
- ` custom string nomos:kind = "domain"`,
353
- ` custom string nomos:domain = ${usdaString(domain)}`,
354
- // STABLE IDS + the captured-read gate (#58): layer-level law keys, hex-carried
386
+ // Stable ids + holon-owned manifests: layer-level law keys, hex-carried
355
387
  // so the .usda round-trips byte-faithfully to the IR layer.
356
- ...(layer.nomosStableIds !== undefined
357
- ? [` custom string nomos:stableIdsHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosStableIds)))}`]
358
- : []),
359
- ...(layer.nomosReadGate !== undefined
360
- ? [` custom string nomos:readGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosReadGate)))}`]
361
- : []),
362
- ...(layer.nomosReadVisibility !== undefined
363
- ? [` custom string nomos:readVisibilityHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosReadVisibility)))}`]
364
- : []),
365
- ...(layer.nomosEncryption !== undefined
366
- ? [` custom string nomos:encryptionHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosEncryption)))}`]
367
- : []),
368
- ...(layer.nomosRelations !== undefined
369
- ? [` custom string nomos:relationsHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosRelations)))}`]
370
- : []),
371
- ...(layer.nomosRelationGate !== undefined
372
- ? [` custom string nomos:relationGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosRelationGate)))}`]
373
- : []),
374
- ...(layer.nomosAttestationGate !== undefined
375
- ? [` custom string nomos:attestationGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosAttestationGate)))}`]
376
- : []),
388
+ ...domainLayerAttrLines(domain, layer, " "),
377
389
  ``,
378
390
  ...body,
379
391
  ` }`,
@@ -388,8 +400,30 @@ export function usdaLayerText(
388
400
  * list is STRONGEST-FIRST, so the list is emitted REVERSED. Same monoid, opposite
389
401
  * notation — the reversal IS the correspondence, recorded in the doc string.
390
402
  */
391
- export function layeredRootUsda(sublayerRelPathsWeakToStrong: readonly string[]): string {
403
+ export function layeredRootUsda(
404
+ sublayerRelPathsWeakToStrong: readonly string[],
405
+ composed?: UsdDocument,
406
+ ): string {
392
407
  const strongestFirst = [...sublayerRelPathsWeakToStrong].reverse();
408
+ const composedDomainOpinions =
409
+ composed === undefined || composed.layers.length === 0
410
+ ? []
411
+ : [
412
+ ``,
413
+ `over "Nomos"`,
414
+ `{`,
415
+ ...composed.layers.flatMap((layer) => {
416
+ const domain = layer.path.slice(layer.path.lastIndexOf("/") + 1);
417
+ return [
418
+ ` over ${usdaString(primName(domain))}`,
419
+ ` {`,
420
+ ...domainLayerAttrLines(domain, layer, " "),
421
+ ` }`,
422
+ ];
423
+ }),
424
+ `}`,
425
+ ``,
426
+ ];
393
427
  return [
394
428
  `#usda 1.0`,
395
429
  `(`,
@@ -406,6 +440,7 @@ export function layeredRootUsda(sublayerRelPathsWeakToStrong: readonly string[])
406
440
  ),
407
441
  ` ]`,
408
442
  `)`,
443
+ ...composedDomainOpinions,
409
444
  ``,
410
445
  ].join("\n");
411
446
  }
@@ -461,6 +496,97 @@ function foldById<T extends { readonly id: string }>(
461
496
  return [...byId.keys()].sort().map((id) => byId.get(id)!);
462
497
  }
463
498
 
499
+ function foldByKey<T>(
500
+ lists: readonly (readonly T[] | undefined)[],
501
+ keyOf: (item: T) => string,
502
+ ): T[] | undefined {
503
+ const byKey = new Map<string, T>();
504
+ for (const list of lists) for (const item of list ?? []) byKey.set(keyOf(item), item);
505
+ if (byKey.size === 0) return undefined;
506
+ return [...byKey.keys()].sort().map((key) => byKey.get(key)!);
507
+ }
508
+
509
+ function foldRecord<T>(
510
+ records: readonly (Readonly<Record<string, T>> | undefined)[],
511
+ ): Record<string, T> | undefined {
512
+ const byKey = new Map<string, T>();
513
+ for (const record of records) {
514
+ if (record === undefined) continue;
515
+ for (const [key, value] of Object.entries(record)) byKey.set(key, value);
516
+ }
517
+ if (byKey.size === 0) return undefined;
518
+ const out: Record<string, T> = {};
519
+ for (const key of [...byKey.keys()].sort()) out[key] = byKey.get(key)!;
520
+ return out;
521
+ }
522
+
523
+ function foldIdentityManifest(
524
+ stack: readonly UsdLayer[],
525
+ stableIds: UsdLayer["nomosStableIds"],
526
+ ): UsdLayer["nomosIdentityManifest"] {
527
+ const manifests = stack
528
+ .map((l) => l.nomosIdentityManifest)
529
+ .filter((m): m is NonNullable<UsdLayer["nomosIdentityManifest"]> => m !== undefined);
530
+ if (manifests.length === 0) return undefined;
531
+ const domain = manifests[manifests.length - 1]!.domain;
532
+ const workspaceInvariants = foldById(manifests.map((m) => m.workspaceInvariants));
533
+ const queries = foldById(manifests.map((m) => m.queries));
534
+ const counts = foldById(manifests.map((m) => m.counts));
535
+ const spatials = foldById(manifests.map((m) => m.spatials));
536
+ const sums = foldById(manifests.map((m) => m.sums));
537
+ const deriveds = foldById(manifests.map((m) => m.deriveds));
538
+ const combineds = foldById(manifests.map((m) => m.combineds));
539
+ const mins = foldById(manifests.map((m) => m.mins));
540
+ const maxes = foldById(manifests.map((m) => m.maxes));
541
+ const orderedReads = foldById(manifests.map((m) => m.orderedReads));
542
+ const workspaceTypes = foldById(manifests.map((m) => m.workspaceTypes));
543
+ const homes = foldRecord(manifests.map((m) => m.homes));
544
+ const routes = foldByKey(manifests.map((m) => m.routes), (r) => r.directive);
545
+ return {
546
+ domain,
547
+ aggregates: foldById(manifests.map((m) => m.aggregates)) ?? [],
548
+ directives: foldById(manifests.map((m) => m.directives)) ?? [],
549
+ ...(stableIds !== undefined ? { stableIds } : {}),
550
+ ...(workspaceInvariants !== undefined ? { workspaceInvariants } : {}),
551
+ ...(queries !== undefined ? { queries } : {}),
552
+ ...(counts !== undefined ? { counts } : {}),
553
+ ...(spatials !== undefined ? { spatials } : {}),
554
+ ...(sums !== undefined ? { sums } : {}),
555
+ ...(deriveds !== undefined ? { deriveds } : {}),
556
+ ...(combineds !== undefined ? { combineds } : {}),
557
+ ...(mins !== undefined ? { mins } : {}),
558
+ ...(maxes !== undefined ? { maxes } : {}),
559
+ ...(orderedReads !== undefined ? { orderedReads } : {}),
560
+ ...(workspaceTypes !== undefined ? { workspaceTypes } : {}),
561
+ ...(homes !== undefined ? { homes } : {}),
562
+ ...(routes !== undefined ? { routes } : {}),
563
+ };
564
+ }
565
+
566
+ function foldReadProjection(stack: readonly UsdLayer[]): UsdLayer["nomosReadProjection"] {
567
+ const projections = stack
568
+ .map((l) => l.nomosReadProjection)
569
+ .filter((p): p is NonNullable<UsdLayer["nomosReadProjection"]> => p !== undefined);
570
+ if (projections.length === 0) return undefined;
571
+ const aggregateFieldKinds = foldRecord(projections.map((p) => p.aggregateFieldKinds)) ?? {};
572
+ const sums = foldById(projections.map((p) => p.sums));
573
+ const mins = foldById(projections.map((p) => p.mins));
574
+ const maxes = foldById(projections.map((p) => p.maxes));
575
+ const orderedReads = foldById(projections.map((p) => p.orderedReads));
576
+ return {
577
+ aggregateFieldKinds,
578
+ queries: foldById(projections.map((p) => p.queries)) ?? [],
579
+ counts: foldById(projections.map((p) => p.counts)) ?? [],
580
+ spatials: foldById(projections.map((p) => p.spatials)) ?? [],
581
+ deriveds: foldById(projections.map((p) => p.deriveds)) ?? [],
582
+ combineds: foldById(projections.map((p) => p.combineds)) ?? [],
583
+ ...(sums !== undefined ? { sums } : {}),
584
+ ...(mins !== undefined ? { mins } : {}),
585
+ ...(maxes !== undefined ? { maxes } : {}),
586
+ ...(orderedReads !== undefined ? { orderedReads } : {}),
587
+ };
588
+ }
589
+
464
590
  /**
465
591
  * THE ORDERED OPINION-MAP FOLD: flatten module layers (weak → strong within each
466
592
  * stage path) into the effective single-layer-per-domain document. Strongest
@@ -509,6 +635,8 @@ export function flattenLayeredUsd(layers: readonly UsdLayer[]): UsdDocument {
509
635
  stableIds = { v: 1, domain: domainSid, aggregates };
510
636
  }
511
637
  }
638
+ const identityManifest = foldIdentityManifest(stack, stableIds);
639
+ const readProjection = foldReadProjection(stack);
512
640
  // The captured-read gate (#58): queries + per-directive read sets merged
513
641
  // later-wins, SORTED keys (matching the composed emission).
514
642
  let readGate: UsdLayer["nomosReadGate"];
@@ -606,6 +734,8 @@ export function flattenLayeredUsd(layers: readonly UsdLayer[]): UsdDocument {
606
734
  return {
607
735
  path,
608
736
  prims: [...prims.keys()].sort().map((p) => prims.get(p)!),
737
+ ...(identityManifest !== undefined ? { nomosIdentityManifest: identityManifest } : {}),
738
+ ...(readProjection !== undefined ? { nomosReadProjection: readProjection } : {}),
609
739
  ...(queries !== undefined ? { queries } : {}),
610
740
  ...(deriveds !== undefined ? { deriveds } : {}),
611
741
  ...(combineds !== undefined ? { combineds } : {}),
@@ -733,6 +863,8 @@ function attrStringArray(prim: ParsedPrim, name: string): string[] {
733
863
  export function parseUsdaDocument(text: string): UsdDocument {
734
864
  const prims = parseUsdaPrims(text);
735
865
  const domainByScope = new Map<string, string>();
866
+ const identityManifestByDomain = new Map<string, UsdLayer["nomosIdentityManifest"]>();
867
+ const readProjectionByDomain = new Map<string, UsdLayer["nomosReadProjection"]>();
736
868
  const stableIdsByDomain = new Map<string, UsdLayer["nomosStableIds"]>();
737
869
  const readGateByDomain = new Map<string, UsdLayer["nomosReadGate"]>();
738
870
  const readVisibilityByDomain = new Map<string, UsdLayer["nomosReadVisibility"]>();
@@ -744,7 +876,15 @@ export function parseUsdaDocument(text: string): UsdDocument {
744
876
  if (p.attrs.get("nomos:kind") === "domain") {
745
877
  const domain = attrString(p, "nomos:domain");
746
878
  domainByScope.set(p.path, domain);
747
- // STABLE IDS + the captured-read gate (#58): the hex-carried layer-level keys.
879
+ // Holon-owned manifests + gates: the hex-carried layer-level law keys.
880
+ const idHex = p.attrs.get("nomos:identityManifestHex");
881
+ if (typeof idHex === "string") {
882
+ identityManifestByDomain.set(domain, JSON.parse(unhexUtf8(idHex)) as UsdLayer["nomosIdentityManifest"]);
883
+ }
884
+ const rpHex = p.attrs.get("nomos:readProjectionHex");
885
+ if (typeof rpHex === "string") {
886
+ readProjectionByDomain.set(domain, JSON.parse(unhexUtf8(rpHex)) as UsdLayer["nomosReadProjection"]);
887
+ }
748
888
  const sidHex = p.attrs.get("nomos:stableIdsHex");
749
889
  if (typeof sidHex === "string") {
750
890
  stableIdsByDomain.set(domain, JSON.parse(unhexUtf8(sidHex)) as UsdLayer["nomosStableIds"]);
@@ -865,6 +1005,8 @@ export function parseUsdaDocument(text: string): UsdDocument {
865
1005
  const layers: UsdLayer[] = [...buckets.keys()]
866
1006
  .map((domain) => {
867
1007
  const b = buckets.get(domain)!;
1008
+ const identity = identityManifestByDomain.get(domain);
1009
+ const readProjection = readProjectionByDomain.get(domain);
868
1010
  const sids = stableIdsByDomain.get(domain);
869
1011
  const rg = readGateByDomain.get(domain);
870
1012
  const rv = readVisibilityByDomain.get(domain);
@@ -875,6 +1017,8 @@ export function parseUsdaDocument(text: string): UsdDocument {
875
1017
  return {
876
1018
  path: `/Nomos/${domain}`,
877
1019
  prims: [...b.prims].sort(byPath),
1020
+ ...(identity !== undefined ? { nomosIdentityManifest: identity } : {}),
1021
+ ...(readProjection !== undefined ? { nomosReadProjection: readProjection } : {}),
878
1022
  ...(b.queries.length > 0 ? { queries: [...b.queries].sort(byId) } : {}),
879
1023
  ...(b.deriveds.length > 0 ? { deriveds: [...b.deriveds].sort(byId) } : {}),
880
1024
  ...(b.combineds.length > 0 ? { combineds: [...b.combineds].sort(byId) } : {}),
@@ -10,7 +10,7 @@
10
10
  * invariants, slice 3 delta lane + least-loaded placement).
11
11
  *
12
12
  * `nomos-compile` APPENDS this module to a TAXONOMY-BEARING domain's module list —
13
- * both in the canonical manifests (the placement law is hash-bearing law) and in the
13
+ * both in the compiled package metadata (the placement law is hash-bearing law) and in the
14
14
  * generated engine entry (the plans ship in the lump). A taxonomy-FREE package never
15
15
  * imports it (subpath `@githolon/dsl/workspace-sharding`, never the runtime barrel —
16
16
  * the slice-1 hash-stability law: its bytes must not enter other tenants' lumps).
@@ -110,7 +110,7 @@ export interface ShardingRouteSpec {
110
110
  export interface ShardingLawSpec {
111
111
  readonly axes: readonly ShardingAxisSpec[];
112
112
  /**
113
- * THE DERIVED ROUTES (slice 2's canonical-manifest `routes`, fed back into the law):
113
+ * THE DERIVED ROUTES (slice 2's package route facts, fed back into the law):
114
114
  * per routed directive, the homing invariant `nomosWrongHome:<directive>` is derived
115
115
  * so THE CHAIN GATE ITSELF refuses a wrong-home write (#41 — slice 3a; the edge
116
116
  * bailiff stays as defense-in-depth). OMITTED routes ⇒ no invariants (slice-2 shape).
@@ -317,7 +317,6 @@ export const NomosSummarySubtotal = aggregate("NomosSummarySubtotal", {
317
317
  export const NomosSummaryFrontier = aggregate("NomosSummaryFrontier", {
318
318
  shard: t.string().merge(Lww),
319
319
  frontier: t.string().merge(Lww), // the shard main oid the subtotals are true at
320
- readManifestHash: t.string().merge(Lww),
321
320
  updatedAt: t.string().merge(Lww),
322
321
  });
323
322
 
@@ -530,7 +529,6 @@ export const nomosAmendSummaryFrontier = directive("nomosAmendSummaryFrontier")
530
529
  z.object({
531
530
  shard: z.string().regex(/^s\d+$/),
532
531
  frontier: z.string(),
533
- readManifestHash: z.string(),
534
532
  updatedAt: z.string(),
535
533
  }),
536
534
  )
@@ -539,7 +537,6 @@ export const nomosAmendSummaryFrontier = directive("nomosAmendSummaryFrontier")
539
537
  return [
540
538
  withMarker(set(wm, "shard", p.shard), "ensures"),
541
539
  set(wm, "frontier", p.frontier),
542
- set(wm, "readManifestHash", p.readManifestHash),
543
540
  set(wm, "updatedAt", p.updatedAt),
544
541
  ];
545
542
  });
@@ -656,7 +653,6 @@ export const nomosPropagateSummary = directive("nomosPropagateSummary")
656
653
  shard: z.string().regex(/^s\d+$/),
657
654
  fromOid: z.string(), // "" = the shard's first delta (a virgin coordinator record)
658
655
  toOid: z.string().min(1),
659
- readManifestHash: z.string(),
660
656
  propagatedAt: z.string(),
661
657
  subtotals: z.array(summarySubtotalSchema).min(1).max(1024),
662
658
  events: z.array(summaryEventSchema).max(4096),
@@ -762,7 +758,6 @@ export const nomosPropagateSummary = directive("nomosPropagateSummary")
762
758
  ops.push(
763
759
  withMarker(set(wm, "shard", p.shard), "ensures"),
764
760
  set(wm, "frontier", p.toOid),
765
- set(wm, "readManifestHash", p.readManifestHash),
766
761
  set(wm, "updatedAt", p.propagatedAt),
767
762
  );
768
763
  return ops;