@githolon/dsl 0.23.1 → 0.24.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@githolon/dsl",
3
- "version": "0.23.1",
3
+ "version": "0.24.1",
4
4
  "type": "module",
5
5
  "description": "Nomos 2 domain-authoring DSL: aggregates + directives in TS, executed and encoded to the Rust kernel's wire shapes.",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -361,6 +361,12 @@ export interface ChildBirthLeg {
361
361
  readonly birthPayload: Record<string, unknown>;
362
362
  /** Required `…At` fields the runtime stamps with ISO now() at dispatch (mirrors the create leg). */
363
363
  readonly birthAutoStamped: readonly string[];
364
+ /** The payload field whose value is the authoring actor for this birth offer, when discoverable from the
365
+ * genesis recipe's step actors. Needed for gated births (`.births().requires(...)`) so proof drives the
366
+ * same actor the generated client would seal as `security.actor`. */
367
+ readonly birthActorField?: string;
368
+ /** The relation the birth offer requires (`.births().requires(...)`), when present. */
369
+ readonly birthRequiresRelation?: string;
364
370
  /** The payload field that carries the bootstrap framework-controller hash (runtime: the local wasm's own
365
371
  * controller hash) — discovered from the recipe's `bootstrap/installDomain` step's payload, by matching
366
372
  * the field whose synthesized value the recipe routed there. `undefined` ⇒ the recipe hard-codes it. */
@@ -379,6 +385,21 @@ export interface ChildBirthLeg {
379
385
  readonly childSeedInstanceId?: string;
380
386
  /** A child declared query that reads the seed back (returns the seed aggregate). `undefined` ⇒ by-id only. */
381
387
  readonly childQuery?: { readonly id: string; readonly key: readonly string[] };
388
+ /** Optional parent setup: when this birth is gated by authority that is established by first birthing the
389
+ * parent workspace (home → estate is the canonical case), the proof runner drives this setup birth first
390
+ * and then authors the child birth inside the born parent. */
391
+ readonly parentSetup?: ChildBirthParentSetup;
392
+ }
393
+
394
+ export interface ChildBirthParentSetup {
395
+ readonly parentDomain: string;
396
+ readonly birthDirectiveId: string;
397
+ readonly birthPayload: Record<string, unknown>;
398
+ readonly birthAutoStamped: readonly string[];
399
+ readonly birthActorField?: string;
400
+ readonly frameworkHashField?: string;
401
+ readonly lawHashField?: string;
402
+ readonly childDomain: string;
382
403
  }
383
404
 
384
405
  /** A stub `nomos` global (mint + sha256) so a `.births()` plan that mints / hashes a child name runs to
@@ -489,6 +510,19 @@ export function discoverChildBirthLeg(
489
510
  const nomosStep = installSteps.find((s) => s.domain === "nomos");
490
511
  const frameworkHashField = bootstrapStep ? fieldForValue(bootstrapStep.payload?.domainHash) : undefined;
491
512
  const lawHashField = nomosStep ? fieldForValue(nomosStep.payload?.domainHash) : undefined;
513
+ const actorField = (() => {
514
+ for (const step of chain as Array<DiscoveredGenesisStep & { actor?: unknown }>) {
515
+ const actor = step?.actor;
516
+ const exact = fieldForValue(actor);
517
+ if (exact !== undefined) return exact;
518
+ if (typeof actor !== "string") continue;
519
+ for (const [field, value] of Object.entries(synth.value)) {
520
+ if (typeof value !== "string" || value.length === 0) continue;
521
+ if (actor === `user:${value}` || actor === `key:${value}`) return field;
522
+ }
523
+ }
524
+ return undefined;
525
+ })();
492
526
 
493
527
  // The parent payload the leg dispatches: the synthesized values MINUS the framework/law-hash placeholders
494
528
  // (the runtime injects the REAL hashes onto those fields by name — a `sample-*` hash would fail the child
@@ -502,6 +536,8 @@ export function discoverChildBirthLeg(
502
536
  birthDirectiveId: birthDir.id,
503
537
  birthPayload,
504
538
  birthAutoStamped: synth.autoStamped,
539
+ ...(actorField !== undefined ? { birthActorField: actorField } : {}),
540
+ ...(birthDir.requiresRelationGate !== undefined ? { birthRequiresRelation: birthDir.requiresRelationGate.relation } : {}),
505
541
  ...(frameworkHashField !== undefined ? { frameworkHashField } : {}),
506
542
  ...(lawHashField !== undefined ? { lawHashField } : {}),
507
543
  childDomain,
@@ -512,6 +548,38 @@ export function discoverChildBirthLeg(
512
548
  };
513
549
  }
514
550
 
551
+ function childBirthsForModule(mod: DomainModule, modules: readonly DomainModule[]): ChildBirthLeg[] {
552
+ const domain = mod.domain ?? mod.name;
553
+ const out: ChildBirthLeg[] = [];
554
+ for (const d of mod.directives as Directive<unknown>[]) {
555
+ if (d?.birthsChild !== true || typeof d.payloadSchema !== "object") continue;
556
+ const leg = discoverChildBirthLeg(d, domain, modules);
557
+ if (leg !== undefined) out.push(leg);
558
+ }
559
+ return out;
560
+ }
561
+
562
+ function asParentSetup(leg: ChildBirthLeg): ChildBirthParentSetup {
563
+ return {
564
+ parentDomain: leg.parentDomain,
565
+ birthDirectiveId: leg.birthDirectiveId,
566
+ birthPayload: leg.birthPayload,
567
+ birthAutoStamped: leg.birthAutoStamped,
568
+ ...(leg.birthActorField !== undefined ? { birthActorField: leg.birthActorField } : {}),
569
+ ...(leg.frameworkHashField !== undefined ? { frameworkHashField: leg.frameworkHashField } : {}),
570
+ ...(leg.lawHashField !== undefined ? { lawHashField: leg.lawHashField } : {}),
571
+ childDomain: leg.childDomain,
572
+ };
573
+ }
574
+
575
+ function attachParentSetup(leg: ChildBirthLeg, allBirths: readonly ChildBirthLeg[]): ChildBirthLeg {
576
+ if (leg.birthRequiresRelation === undefined) return leg;
577
+ const setup = allBirths.find((candidate) =>
578
+ candidate.childDomain === leg.parentDomain &&
579
+ !(candidate.parentDomain === leg.parentDomain && candidate.birthDirectiveId === leg.birthDirectiveId));
580
+ return setup === undefined ? leg : { ...leg, parentSetup: asParentSetup(setup) };
581
+ }
582
+
515
583
  /** Discover the SINGLETON instance id a seed directive writes (a fixed `<Agg>:self`-shaped id), by running
516
584
  * its plan with a probe payload + birth-probe globals and reading the first written op's instance id when
517
585
  * it is a constant (the same id across two distinct probe payloads). `undefined` ⇒ not a fixed singleton. */
@@ -644,6 +712,7 @@ function birthDirectiveOf(mod: DomainModule): Directive<unknown> | undefined {
644
712
  * domain is at least `compile-valid` (gaps 1+3 — every domain is proof-addressable). */
645
713
  export function legsForDomain(mod: DomainModule, modules: readonly DomainModule[]): ProofLegs {
646
714
  const domain = mod.domain ?? mod.name;
715
+ const allBirths = modules.flatMap((m) => childBirthsForModule(m, modules));
647
716
  // The standalone-create leg (when the domain has a non-birth `.creates`). FAIL SOFT: an exotic payload
648
717
  // shape the synthesizer can't sample just drops the standalone leg (the domain stays compile-valid /
649
718
  // child-birth-provable), never loses the index.
@@ -655,11 +724,12 @@ export function legsForDomain(mod: DomainModule, modules: readonly DomainModule[
655
724
  }
656
725
  // The child-birth leg (when the domain has a `.births()` directive) — discovered by running its plan.
657
726
  const birthDir = birthDirectiveOf(mod);
658
- const childBirth = birthDir !== undefined ? discoverChildBirthLeg(birthDir, domain, modules) : undefined;
727
+ const childBirth = birthDir !== undefined ? allBirths.find((b) => b.parentDomain === domain && b.birthDirectiveId === birthDir.id) : undefined;
728
+ const childBirthWithSetup = childBirth !== undefined ? attachParentSetup(childBirth, allBirths) : undefined;
659
729
 
660
730
  const legs: ProofKind[] = ["compile-valid"];
661
731
  if (standalone !== null) legs.push("standalone-provable");
662
- if (childBirth !== undefined) legs.push("child-birth-provable");
732
+ if (childBirthWithSetup !== undefined) legs.push("child-birth-provable");
663
733
 
664
734
  return {
665
735
  domain,
@@ -675,7 +745,7 @@ export function legsForDomain(mod: DomainModule, modules: readonly DomainModule[
675
745
  ...(standalone.count !== undefined ? { count: standalone.count } : {}),
676
746
  }
677
747
  : {}),
678
- ...(childBirth !== undefined ? { childBirth } : {}),
748
+ ...(childBirthWithSetup !== undefined ? { childBirth: childBirthWithSetup } : {}),
679
749
  };
680
750
  }
681
751
 
@@ -798,26 +868,65 @@ function liveChildBirthLegLines(cb: ChildBirthLeg, opts: TsProofOptions, n: () =
798
868
  // The birth payload literal: the synthesized values + the REAL hashes onto the discovered fields + the
799
869
  // auto-stamped `…At` fields stamped now.
800
870
  const lines: string[] = [];
871
+ const setup = cb.parentSetup;
872
+ const actorExpr = (field: string | undefined, payloadVar: string): string | undefined =>
873
+ field !== undefined ? `String(${payloadVar}[${JSON.stringify(field)}] ?? "")` : undefined;
874
+ const birthActorExpr = actorExpr(cb.birthActorField, "birthPayload");
801
875
  lines.push(
802
876
  `// ${n()}. CHILD BIRTH (G3 — birth is the defined consequence of an offer): drive \`${cb.parentDomain}/${cb.birthDirectiveId}\``,
803
877
  `// against the cloud; the kernel's offer-effect installs the child law in-chain + folds the child's`,
804
878
  `// genesis (estate/${cb.childSeedDirectiveId}) through the CHILD's own gate, then BORNS the child workspace.`,
805
879
  `{`,
880
+ ` const frameworkHashFor = async (workspace: string) => {`,
881
+ ` const st = await (await fetch(\`\${CLOUD}/v2/workspaces/\${workspace}\`)).json().catch(() => ({}));`,
882
+ ` const frameworkHash = st.controller?.domainHash ?? st.controllerHash ?? "";`,
883
+ ` if (!frameworkHash) fail(\`could not resolve the framework controller hash from workspace \${workspace} — needed for the child's in-chain installDomain\`);`,
884
+ ` return frameworkHash;`,
885
+ ` };`,
806
886
  ` const birthPayload: Record<string, unknown> = { ...${JSON.stringify(cb.birthPayload)} };`,
887
+ ...(
888
+ birthActorExpr !== undefined && cb.birthRequiresRelation !== undefined
889
+ ? [
890
+ ` const birthActor = ${birthActorExpr};`,
891
+ ` if (!birthActor) fail(${JSON.stringify(`child birth ${cb.parentDomain}/${cb.birthDirectiveId} is gated by .requires("${cb.birthRequiresRelation}") but the proof could not derive an actor from ${cb.birthActorField}`)});`,
892
+ ]
893
+ : [
894
+ ` const birthActor = PRINCIPAL;`,
895
+ ]
896
+ ),
897
+ ...(setup !== undefined
898
+ ? [
899
+ ` // Parent setup: this child birth is gated by authority seeded when the parent workspace is born.`,
900
+ ` const setupPayload: Record<string, unknown> = { ...${JSON.stringify(setup.birthPayload)} };`,
901
+ ...(setup.birthActorField !== undefined && cb.birthActorField !== undefined
902
+ ? [` setupPayload[${JSON.stringify(setup.birthActorField)}] = birthPayload[${JSON.stringify(cb.birthActorField)}];`]
903
+ : []),
904
+ ...(setup.frameworkHashField !== undefined ? [` setupPayload[${JSON.stringify(setup.frameworkHashField)}] = await frameworkHashFor(WS);`] : []),
905
+ ...(setup.lawHashField !== undefined ? [` setupPayload[${JSON.stringify(setup.lawHashField)}] = ${hashConst};`] : []),
906
+ ...setup.birthAutoStamped.map((f) => ` setupPayload[${JSON.stringify(f)}] = new Date().toISOString();`),
907
+ ` const setupActor = ${actorExpr(setup.birthActorField, "setupPayload") ?? "birthActor"} || birthActor;`,
908
+ ` const sr = await fetch(\`\${CLOUD}/v2/workspaces/\${WS}/${setup.birthDirectiveId}\`, { method: "POST", headers: { "content-type": "application/json", "x-nomos-principal": setupActor }, body: JSON.stringify(setupPayload) });`,
909
+ ` const sd = await sr.json();`,
910
+ ` if (!sd.ok) fail(\`parent setup ${setup.parentDomain}/${setup.birthDirectiveId} refused: \${JSON.stringify(sd)}\`);`,
911
+ ` const setupBorn: Array<{ workspace?: string }> = sd.born ?? [];`,
912
+ ` const birthParentWs = setupBorn[0]?.workspace;`,
913
+ ` if (!birthParentWs) fail(\`parent setup admitted but spawned no workspace (born=\${JSON.stringify(setupBorn)})\`);`,
914
+ ` ok(\`parent setup — \${birthParentWs} born by ${setup.parentDomain}/${setup.birthDirectiveId} before gated child birth\`);`,
915
+ ]
916
+ : [
917
+ ` const birthParentWs = WS;`,
918
+ ]),
807
919
  ...(cb.frameworkHashField !== undefined
808
920
  ? [
809
921
  ` // The framework CONTROLLER hash (the bootstrap install-target the child runs as intent 0) — read it`,
810
922
  ` // from the workspace's own status (the kernel's controllerHash); the child install resolves the bytes`,
811
923
  ` // by hash from the parent's custody, so this MUST be the real framework hash (a guess would fail).`,
812
- ` const st = await (await fetch(\`\${CLOUD}/v2/workspaces/\${WS}\`)).json().catch(() => ({}));`,
813
- ` const frameworkHash = st.controller?.domainHash ?? st.controllerHash ?? "";`,
814
- ` if (!frameworkHash) fail("could not resolve the framework controller hash from the workspace status — needed for the child's in-chain installDomain");`,
815
- ` birthPayload[${JSON.stringify(cb.frameworkHashField)}] = frameworkHash;`,
924
+ ` birthPayload[${JSON.stringify(cb.frameworkHashField)}] = await frameworkHashFor(birthParentWs);`,
816
925
  ]
817
926
  : []),
818
927
  ` birthPayload[${JSON.stringify(cb.lawHashField ?? "lawHash")}] = ${hashConst};`,
819
928
  ...cb.birthAutoStamped.map((f) => ` birthPayload[${JSON.stringify(f)}] = new Date().toISOString();`),
820
- ` const br = await fetch(\`\${CLOUD}/v2/workspaces/\${WS}/${cb.birthDirectiveId}\`, { method: "POST", headers: { "content-type": "application/json", "x-nomos-principal": PRINCIPAL }, body: JSON.stringify(birthPayload) });`,
929
+ ` const br = await fetch(\`\${CLOUD}/v2/workspaces/\${birthParentWs}/${cb.birthDirectiveId}\`, { method: "POST", headers: { "content-type": "application/json", "x-nomos-principal": birthActor }, body: JSON.stringify(birthPayload) });`,
821
930
  ` const bd = await br.json();`,
822
931
  ` if (!bd.ok) fail(\`child birth offer ${cb.parentDomain}/${cb.birthDirectiveId} refused: \${JSON.stringify(bd)}\`);`,
823
932
  ` const born: Array<{ workspace?: string }> = bd.born ?? [];`,