@githolon/dsl 0.16.3 → 0.18.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/index.ts CHANGED
@@ -94,6 +94,18 @@ export { create, existing, ensure, type AggregateRef } from "./authoring.js";
94
94
  // `nomos-compile` REFUSES law that references read() (a named compile error, never a runtime halt).
95
95
  // The captured-reads wave (task #58) wires the capability in and lifts the refusal.
96
96
  export { read } from "./read.js";
97
+ // THE BIRTH EFFECT (G3 — birth is a declared kernel primitive): a `.births()` directive's plan records the
98
+ // child's genesis recipe via `birth(recipe)` (reuse `buildGenesisChain`); it rides the committed intent as a
99
+ // declared output, and the kernel — reading the `.births` declaration off the law manifest (the
100
+ // `nomosBirthGate` era key, never domain state) — spawns + self-validates the child after the offer admits.
101
+ export { birth, type BirthRecipe } from "./births.js";
102
+ // THE PEER-SOVEREIGN ROOT-OF-TRUST (the BirthAttestation cert chain): a home/child law exports
103
+ // `requireParentAttestedBirth` to declare it requires a parent-attested-birth cert chain (the
104
+ // `nomosParentAttestedBirth` era key); the kernel verify doors then verify the travelling chain to K_root.
105
+ export {
106
+ requireParentAttestedBirth,
107
+ type ParentAttestedBirthMarker,
108
+ } from "./births.js";
97
109
  export {
98
110
  query,
99
111
  hasManyIndexes,
@@ -247,6 +259,28 @@ export {
247
259
  signerObject,
248
260
  SIGNER_RELATION,
249
261
  enrollSigner,
262
+ // VA-core — SignerEpoch + self-bind enroll/revoke + the strict-increment & verified-enroll gates.
263
+ SignerEpoch,
264
+ SIGNER_EPOCH_ID,
265
+ signerEpochCurrent,
266
+ advanceSignerEpoch,
267
+ signerEpochStrictlyIncreases,
268
+ enrollSignerSelf,
269
+ enrollSignerSelfRequiresVerified,
270
+ revokeSigner,
271
+ revokeSignerEpochStrictlyIncreases,
272
+ signerTuplesByObject,
273
+ signerTupleInstanceId,
274
+ // THE ENROLL-ONCE DELEGATION (the peer-sovereign root-of-trust) — the per-user tenant-signed cert the
275
+ // kernel reads to assemble U's home cert chain at birth.
276
+ SignerDelegation,
277
+ SIGNER_DELEGATION_PREFIX,
278
+ signerDelegationInstanceId,
279
+ enrollDelegation,
280
+ signerDelegationByUser,
281
+ type SignerDelegationHandle,
282
+ type SignerEpochHandle,
283
+ type SignerAuthority,
250
284
  } from "./framework/signing_identity.js";
251
285
  // THE HOME HOLON (home_holons_and_cross_custody.md §4) — the identity-role law: the shared-with-me access
252
286
  // index + the owner lane to maintain it (the key directory is the `signer` relation above).
@@ -262,6 +296,14 @@ export {
262
296
  seedHomeOwner,
263
297
  writeHomeOwner,
264
298
  homeNameBindsOwner,
299
+ homeNameOf,
300
+ birthHome,
301
+ // THE PEER-SOVEREIGN ROOT-OF-TRUST (the BirthAttestation cert chain).
302
+ BirthAttestation,
303
+ BIRTH_ATTESTATION_ID,
304
+ seedBirthAttestation,
305
+ birthAttestation,
306
+ parentAttestedBirth,
265
307
  } from "./framework/home.js";
266
308
  // THE GENESIS WARRANT (identity_keys.md step 0) — the in-history birth fact that lets
267
309
  // the gate's genesis clause admit a founding control-plane grant on a virgin chain.
package/src/usd.ts CHANGED
@@ -285,6 +285,33 @@ export interface UsdLayer {
285
285
  }
286
286
  >;
287
287
  };
288
+ /**
289
+ * THE BIRTH GATE DECLARATION (G3 — birth is a declared kernel primitive, the unix model). Present IFF ≥1
290
+ * directive declares `.births()`: the SORTED set of their ids. A directive named here BIRTHS a child
291
+ * workspace as the defined CONSEQUENCE of its offer — after the offer admits, the kernel reads THIS
292
+ * declaration (the SAME way it reads `nomosReadGate`/`nomosInvariantGate` — never a domain aggregate),
293
+ * takes the genesis recipe the intent carried (its `captured_ports.births`), spawns the child's custody,
294
+ * and self-validates the child's whole genesis through the child's OWN gate (the `materialize_genesis`
295
+ * keystone). The kernel reads NO domain schema; it reads its own manifest declaration + the carried
296
+ * recipe. OMITTED for law with no birth directive (the era rule — byte-identical hash; every pre-feature
297
+ * chain replays green, the birth effect inert). SORTED keys for byte-determinism.
298
+ */
299
+ readonly nomosBirthGate?: {
300
+ readonly v: 1;
301
+ /** The directive ids declared `.births()` — SORTED. */
302
+ readonly directives: string[];
303
+ };
304
+ /**
305
+ * THE PEER-SOVEREIGN ROOT-OF-TRUST (the BirthAttestation cert chain). Present IFF the law declares
306
+ * `requireParentAttestedBirth`: a warranted workspace running this law MUST travel with a SIGNED cert
307
+ * chain proving its founder was VERIFIED by its parent at birth, rooting at the pinned `K_root`. The
308
+ * kernel's verify doors (author / receive / verify_chain) verify the travelling chain OFFLINE — no
309
+ * cross-ws read. OMITTED for law that doesn't declare it (the era rule — byte-identical hash; every
310
+ * pre-feature + legacy/non-warranted chain replays green, the check inert).
311
+ */
312
+ readonly nomosParentAttestedBirth?: {
313
+ readonly v: 1;
314
+ };
288
315
  /**
289
316
  * STABLE IDENTIFIERS (#58 — names are labels, identity is minted). The layer's
290
317
  * domain/aggregate/field sids + label lineages, carried verbatim from the canonical
@@ -721,6 +748,21 @@ export function emitUsd(
721
748
  }
722
749
  const nomosAttestationGate =
723
750
  Object.keys(attestationReq).length > 0 ? { v: 1 as const, directives: attestationReq } : undefined;
751
+ // THE BIRTH GATE (G3 — birth is a declared kernel primitive): the SORTED set of directive ids
752
+ // declaring `.births()`. The kernel reads this off the manifest (never a domain aggregate) to know an
753
+ // admitted directive births, then folds the recipe the intent carried. Omit-when-empty (era rule —
754
+ // a birth-free layer is byte-identical apart from the always-on stableIds key).
755
+ const birthDirectives = [...l.module.directives]
756
+ .filter((d) => (d as { birthsChild?: boolean }).birthsChild === true)
757
+ .map((d) => d.id)
758
+ .sort();
759
+ const nomosBirthGate =
760
+ birthDirectives.length > 0 ? { v: 1 as const, directives: birthDirectives } : undefined;
761
+ // THE PEER-SOVEREIGN ROOT-OF-TRUST: the home/child law declared it requires a parent-attested-birth
762
+ // cert chain (the `requireParentAttestedBirth` sentinel, discovered by composeDomainModule). Emit the
763
+ // era key iff true; omit otherwise (a law that doesn't require it is byte-identical — the era rule).
764
+ const nomosParentAttestedBirth =
765
+ (l.module as { parentAttestedBirth?: boolean }).parentAttestedBirth === true ? { v: 1 as const } : undefined;
724
766
  // THE CAPTURED-READ GATE DECLARATION (#58): collected from the canonical
725
767
  // manifest's per-directive `capturedReads` (omit-when-empty — read-free law's
726
768
  // USD is byte-identical apart from the always-on stableIds key).
@@ -850,6 +892,8 @@ export function emitUsd(
850
892
  ...(nomosRelations !== undefined ? { nomosRelations } : {}),
851
893
  ...(nomosRelationGate !== undefined ? { nomosRelationGate } : {}),
852
894
  ...(nomosAttestationGate !== undefined ? { nomosAttestationGate } : {}),
895
+ ...(nomosBirthGate !== undefined ? { nomosBirthGate } : {}),
896
+ ...(nomosParentAttestedBirth !== undefined ? { nomosParentAttestedBirth } : {}),
853
897
  ...(Object.keys(visAggregates).length > 0
854
898
  ? { nomosReadVisibility: { v: 1 as const, aggregates: visAggregates } }
855
899
  : {}),
package/src/usd_layers.ts CHANGED
@@ -177,6 +177,12 @@ function domainLayerAttrLines(domain: string, layer: UsdLayer, indent: string):
177
177
  ...(layer.nomosAttestationGate !== undefined
178
178
  ? [`${indent}custom string nomos:attestationGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosAttestationGate)))}`]
179
179
  : []),
180
+ ...(layer.nomosBirthGate !== undefined
181
+ ? [`${indent}custom string nomos:birthGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosBirthGate)))}`]
182
+ : []),
183
+ ...(layer.nomosParentAttestedBirth !== undefined
184
+ ? [`${indent}custom string nomos:parentAttestedBirthHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosParentAttestedBirth)))}`]
185
+ : []),
180
186
  ];
181
187
  }
182
188
 
@@ -765,6 +771,23 @@ export function flattenLayeredUsd(layers: readonly UsdLayer[]): UsdDocument {
765
771
  attestationGate = { v: 1, directives: rec };
766
772
  }
767
773
  }
774
+ // THE BIRTH GATE (G3): the SORTED union of every layer's `.births()` directive ids. Omit-when-empty
775
+ // (era rule). The set is the contract; order is incidental (sorted for byte-determinism).
776
+ let birthGate: UsdLayer["nomosBirthGate"];
777
+ {
778
+ const b = new Set<string>();
779
+ for (const l of stack) {
780
+ if (l.nomosBirthGate === undefined) continue;
781
+ for (const id of l.nomosBirthGate.directives) b.add(id);
782
+ }
783
+ if (b.size > 0) birthGate = { v: 1, directives: [...b].sort() };
784
+ }
785
+ // THE PEER-SOVEREIGN ROOT-OF-TRUST: the era key is present iff ANY layer in the stack declares it
786
+ // (a boolean union — once required, required). Omit-when-absent (era rule).
787
+ let parentAttestedBirth: UsdLayer["nomosParentAttestedBirth"];
788
+ if (stack.some((l) => l.nomosParentAttestedBirth !== undefined)) {
789
+ parentAttestedBirth = { v: 1 };
790
+ }
768
791
  return {
769
792
  path,
770
793
  prims: [...prims.keys()].sort().map((p) => prims.get(p)!),
@@ -781,6 +804,8 @@ export function flattenLayeredUsd(layers: readonly UsdLayer[]): UsdDocument {
781
804
  ...(relations !== undefined ? { nomosRelations: relations } : {}),
782
805
  ...(relationGate !== undefined ? { nomosRelationGate: relationGate } : {}),
783
806
  ...(attestationGate !== undefined ? { nomosAttestationGate: attestationGate } : {}),
807
+ ...(birthGate !== undefined ? { nomosBirthGate: birthGate } : {}),
808
+ ...(parentAttestedBirth !== undefined ? { nomosParentAttestedBirth: parentAttestedBirth } : {}),
784
809
  };
785
810
  });
786
811
 
@@ -908,6 +933,8 @@ export function parseUsdaDocument(text: string): UsdDocument {
908
933
  const relationsByDomain = new Map<string, UsdLayer["nomosRelations"]>();
909
934
  const relationGateByDomain = new Map<string, UsdLayer["nomosRelationGate"]>();
910
935
  const attestationGateByDomain = new Map<string, UsdLayer["nomosAttestationGate"]>();
936
+ const birthGateByDomain = new Map<string, UsdLayer["nomosBirthGate"]>();
937
+ const parentAttestedBirthByDomain = new Map<string, UsdLayer["nomosParentAttestedBirth"]>();
911
938
  for (const p of prims) {
912
939
  if (p.attrs.get("nomos:kind") === "domain") {
913
940
  const domain = attrString(p, "nomos:domain");
@@ -959,6 +986,16 @@ export function parseUsdaDocument(text: string): UsdDocument {
959
986
  if (typeof attGateHex === "string") {
960
987
  attestationGateByDomain.set(domain, JSON.parse(unhexUtf8(attGateHex)) as UsdLayer["nomosAttestationGate"]);
961
988
  }
989
+ // THE BIRTH GATE (G3): the hex-carried `.births()` directive set.
990
+ const birthGateHex = p.attrs.get("nomos:birthGateHex");
991
+ if (typeof birthGateHex === "string") {
992
+ birthGateByDomain.set(domain, JSON.parse(unhexUtf8(birthGateHex)) as UsdLayer["nomosBirthGate"]);
993
+ }
994
+ // THE PEER-SOVEREIGN ROOT-OF-TRUST: the hex-carried parent-attested-birth era marker.
995
+ const pabHex = p.attrs.get("nomos:parentAttestedBirthHex");
996
+ if (typeof pabHex === "string") {
997
+ parentAttestedBirthByDomain.set(domain, JSON.parse(unhexUtf8(pabHex)) as UsdLayer["nomosParentAttestedBirth"]);
998
+ }
962
999
  }
963
1000
  }
964
1001
 
@@ -1056,6 +1093,8 @@ export function parseUsdaDocument(text: string): UsdDocument {
1056
1093
  const rel = relationsByDomain.get(domain);
1057
1094
  const relGate = relationGateByDomain.get(domain);
1058
1095
  const attGate = attestationGateByDomain.get(domain);
1096
+ const birthGate = birthGateByDomain.get(domain);
1097
+ const parentAttestedBirth = parentAttestedBirthByDomain.get(domain);
1059
1098
  return {
1060
1099
  path: `/Nomos/${domain}`,
1061
1100
  prims: [...b.prims].sort(byPath),
@@ -1072,6 +1111,8 @@ export function parseUsdaDocument(text: string): UsdDocument {
1072
1111
  ...(rel !== undefined ? { nomosRelations: rel } : {}),
1073
1112
  ...(relGate !== undefined ? { nomosRelationGate: relGate } : {}),
1074
1113
  ...(attGate !== undefined ? { nomosAttestationGate: attGate } : {}),
1114
+ ...(birthGate !== undefined ? { nomosBirthGate: birthGate } : {}),
1115
+ ...(parentAttestedBirth !== undefined ? { nomosParentAttestedBirth: parentAttestedBirth } : {}),
1075
1116
  };
1076
1117
  })
1077
1118
  .sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
package/src/wire.ts CHANGED
@@ -40,6 +40,10 @@ export type WireValue =
40
40
  | { Set: string[] }
41
41
  | { Map: Record<string, WireField> }
42
42
  | { Evidence: WireEvidenceRef }
43
+ // First-class canonical JSON (RFC 8785 / JCS) — the committed form of a `t.json()` leaf. The
44
+ // value rides INLINE (`{Json: <any json>}`), not as a string-of-JSON; the kernel canonicalises
45
+ // it at the boundary (`canonical_json.rs`) and decodes the variant directly into structured JSON.
46
+ | { Json: unknown }
43
47
  // ── STRUCTURAL CRDT state values (only seen when reading RAW folded state back; the read MODEL
44
48
  // collapses them via the kernel `observed_value` to Int/Str/Set, so typed reads stay scalar) ──
45
49
  | { Counter: Record<string, { pos: number; neg: number }> }
@@ -135,6 +139,15 @@ export interface WireIntent {
135
139
  * empty so a principal-free intent is byte-identical.
136
140
  */
137
141
  endorse?: { sink: string; facet: string; reason: string | null }[];
142
+ /**
143
+ * THE BIRTH EFFECT (G3 — birth is a declared kernel primitive, the unix model): the genesis recipe(s) a
144
+ * `.births()` directive's plan produced (`birth(recipe)`). It rides the intent as a DECLARED OUTPUT — the
145
+ * kernel reads the `.births` DECLARATION off the law manifest (the `nomosBirthGate` era key, NOT domain
146
+ * state), then takes this recipe off the admitted intent and spawns + self-validates the child through the
147
+ * child's OWN gate. A verify-input WITNESS (rides the carried `captured_ports`, never the events nor the
148
+ * OID). OMITTED when empty so a birth-free intent is byte-identical to before this existed (the era rule).
149
+ */
150
+ births?: { workspace: string; genesisChain: unknown }[];
138
151
  }
139
152
 
140
153
  /**
@@ -22,6 +22,7 @@
22
22
  import { encodeDriver } from "./drivers.js";
23
23
  import { __resetAuthoring, __drainAuthoring } from "./authoring.js";
24
24
  import { __resetReads, __drainReads } from "./read.js";
25
+ import { __resetBirths, __drainBirths } from "./births.js";
25
26
  import type { AggregateHandle } from "./aggregate.js";
26
27
  import type { Field, EvidenceRefValue } from "./fields.js";
27
28
  import type { Directive, ReferentialMarker } from "./directive.js";
@@ -86,14 +87,18 @@ function encodeKernelValue(field: Field | undefined, value: unknown): WireValue
86
87
  },
87
88
  };
88
89
  }
90
+ // A `t.json()` leaf lowers to the FIRST-CLASS kernel `Value::Json` (RFC 8785 / JCS), NOT a
91
+ // `Value::Str` holding JSON-shaped text. Checked BEFORE the scalar fallthroughs so EVERY json
92
+ // value — object, array, string, number, boolean, null — commits as one consistent value kind
93
+ // (the kernel canonicalises it at the boundary). This is the new-era encoding; a package compiled
94
+ // before this change froze the old `{ Str: JSON.stringify(value) }` lump and replays unchanged.
95
+ if (field?.kind === "json") return { Json: value };
89
96
  // For scalar Set/SetEntry the kernel expects Str or Int. Maps store leaves;
90
97
  // a map's entry value is a leaf scalar.
91
98
  if (typeof value === "number" && Number.isInteger(value)) return { Int: value };
92
99
  if (typeof value === "string") return { Str: value };
93
100
  if (typeof value === "boolean") return { Str: String(value) };
94
101
  // Enum values are strings; refs are id strings — both handled above.
95
- // Anything else (object/array as a scalar) is JSON-encoded into a Str leaf.
96
- if (field?.kind === "json") return { Str: JSON.stringify(value) };
97
102
  throw new Error(
98
103
  `Cannot encode value ${JSON.stringify(value)} for field '${field?.kind ?? "?"}': ` +
99
104
  `kernel scalars are Str | Int only.`,
@@ -196,9 +201,15 @@ export function executeDirectiveToIntent<P>(
196
201
  // The plan may also READ (O(1) DSL queries via `read(...)`) — captured into the footprint below.
197
202
  __resetAuthoring();
198
203
  __resetReads();
204
+ __resetBirths();
199
205
  const returned = directive.plan(parsed, ctx);
200
206
  const planned = [...__drainAuthoring(), ...returned];
201
207
  const reads = __drainReads();
208
+ // THE BIRTH EFFECT (G3 — birth is a declared kernel primitive): a `.births()` directive's plan records
209
+ // the child's genesis recipe via `birth(recipe)`; it rides the intent as a DECLARED output (like a
210
+ // captured read), drained here. Empty for every non-birth directive — OMITTED below so a birth-free
211
+ // intent is byte-identical (the era rule).
212
+ const births = __drainBirths();
202
213
 
203
214
  // Partition the plan output: STRIKE ops route onto the intent's `strikes` channel
204
215
  // (the kernel's strikeout / revert facet, folded by parity); FIELD ops group into
@@ -302,6 +313,7 @@ export function executeDirectiveToIntent<P>(
302
313
  events,
303
314
  ...(reads.length ? { reads } : {}),
304
315
  ...(endorse.length ? { endorse } : {}),
316
+ ...(births.length ? { births } : {}),
305
317
  };
306
318
  }
307
319