@githolon/dsl 0.29.0 → 0.29.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/codegen_proof.ts +116 -27
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@githolon/dsl",
3
- "version": "0.29.0",
3
+ "version": "0.29.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",
@@ -696,13 +696,17 @@ export function resolveGrantableBaseRelations(
696
696
  export function discoverRelationBootstrapLeg(mod: DomainModule): RelationBootstrapLeg | undefined {
697
697
  const domain = mod.domain ?? mod.name;
698
698
  const directives = mod.directives as Directive<unknown>[];
699
- const gated = directives.find(
700
- (d) =>
701
- d?.requiresRelationGate !== undefined &&
702
- typeof d.payloadSchema === "object" &&
703
- d.birthsChild !== true &&
704
- d.attestationAuthorizedGate === undefined,
705
- );
699
+ const isGatedProvable = (d: Directive<unknown> | undefined): boolean =>
700
+ d?.requiresRelationGate !== undefined &&
701
+ typeof d.payloadSchema === "object" &&
702
+ d.birthsChild !== true &&
703
+ d.attestationAuthorizedGate === undefined;
704
+ // PREFER a gated `.creates` as the bootstrap target: a gated create is exactly the shape excluded from the
705
+ // naive standalone-create leg (isStandaloneCreate), so the bootstrap leg is its ONLY positive proof — seed
706
+ // authority, then prove the CREATE is admitted. When no gated create exists, fall back to any gated write
707
+ // (a gated `.mutates`/`.ensures`), so an un-created gated law is still proven.
708
+ const gated = directives.find((d) => isGatedProvable(d) && d.marker === "creates")
709
+ ?? directives.find((d) => isGatedProvable(d));
706
710
  if (gated === undefined) return undefined;
707
711
  const relation = gated.requiresRelationGate!.relation;
708
712
 
@@ -1185,13 +1189,17 @@ export interface ProofLegs {
1185
1189
  * The SINGLE source of the create-leg selection — `generateTsProof` (the .mts marker) and
1186
1190
  * `proofLegsIndex` (the per-domain index) both call this, so the offline lane proves any domain the
1187
1191
  * same way the generated proof proves its primary one. */
1188
- /** A `.creates` directive the proof can DRIVE from a synthesized sample offline: not a `.births()` (a birth
1189
- * is the child-birth lane), not ATTESTATION-AUTHORIZED (needs a signed §3 quote no synthesizer can produce —
1190
- * e.g. the home law's `recordShare`). Those are NOT standalone-provable offline; the domain proves via its
1191
- * child-birth leg / compile-valid instead. */
1192
+ /** A `.creates` directive the proof can DRIVE from a synthesized sample offline WITH NO SEEDED AUTHORITY: not
1193
+ * a `.births()` (a birth is the child-birth lane), not ATTESTATION-AUTHORIZED (needs a signed §3 quote no
1194
+ * synthesizer can produce — e.g. the home law's `recordShare`), and NOT AUTHZ-GATED (`.requires(R)` — a
1195
+ * gated create dispatched with no seeded authority is REFUSED at the gate; its metadata already says it
1196
+ * needs authority, so the naive standalone leg must not touch it — it is proven POSITIVELY by the
1197
+ * relation-bootstrap leg / the born-child genesis instead). Those are NOT standalone-provable offline; the
1198
+ * domain proves via its bootstrap / child-birth / compile-valid legs instead. */
1192
1199
  function isStandaloneCreate(d: Directive<unknown> | undefined): boolean {
1193
1200
  return d?.marker === "creates" && typeof d.payloadSchema === "object"
1194
- && d.birthsChild !== true && d.attestationAuthorizedGate === undefined;
1201
+ && d.birthsChild !== true && d.attestationAuthorizedGate === undefined
1202
+ && d.requiresRelationGate === undefined;
1195
1203
  }
1196
1204
 
1197
1205
  export function proofLegsForModule(mod: DomainModule): ProofLegs | null {
@@ -1545,19 +1553,94 @@ function generateBirthOnlyProof(
1545
1553
  return out.join("\n");
1546
1554
  }
1547
1555
 
1556
+ /**
1557
+ * THE BOOTSTRAP-ONLY PROOF (gated-create isolation): a package whose ONLY creating write is AUTHZ-GATED
1558
+ * (`.creates(...).requires(R)` — co2's estate `createSite`). It has no ungated standalone create to dispatch
1559
+ * naively (that would be REFUSED at the gate with no seeded authority) and no `.births()` genesis. So the
1560
+ * proof deploys the law, connects, and drives the RELATION-BOOTSTRAP leg as its primary write: seed authority
1561
+ * (layers 1-3), THEN prove the gated create is ADMITTED — the ordering (seed BEFORE the gated dispatch) is
1562
+ * correct by construction. If authority genuinely can't be seeded, the leg emits the loud diagnostic (never a
1563
+ * false green). The offline runner proves the same leg locally (`githolon proof`); this `.mts` is `--live`.
1564
+ */
1565
+ function generateBootstrapOnlyProof(
1566
+ mod: DomainModule,
1567
+ opts: TsProofOptions,
1568
+ primaryLegs: ProofLegs,
1569
+ ): string {
1570
+ const domain = mod.domain ?? mod.name;
1571
+ const factory = tsClientFactoryName(domain);
1572
+ const hashConst = tsHashConstName(opts.packageName);
1573
+ const rb = primaryLegs.relationBootstrap;
1574
+ if (rb === undefined) {
1575
+ // Only reached defensively — the caller gates on relationBootstrap !== undefined.
1576
+ throw new Error(`domain '${domain}' has a gated create but no relation-bootstrap leg to prove it (no seedable authority)`);
1577
+ }
1578
+ const slug =
1579
+ opts.packageName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 24) || "domain";
1580
+ const legsMarker = JSON.stringify(primaryLegs);
1581
+ let step = 0;
1582
+ const n = () => ++step;
1583
+ const out: string[] = [];
1584
+ out.push(
1585
+ `// AUTO-GENERATED by nomos-compile — the RUNNABLE PROOF for package "${opts.packageName}",`,
1586
+ `// synthesized from the domain's OWN law. This package's ONLY creating write is AUTHZ-GATED`,
1587
+ `// (\`${domain}/${rb.gatedDirectiveId}\` .requires("${rb.relation}")), so the proof SEEDS authority first,`,
1588
+ `// then proves the gated create is admitted — never a naive dispatch the gate would refuse. Reshape the`,
1589
+ `// law, recompile, and this proof reshapes itself — no test rewriting. Run it:`,
1590
+ `//`,
1591
+ `// npx githolon proof (offline legs on a local holon)`,
1592
+ `// npx githolon proof --live (or: npx tsx build/${opts.packageName}.proof.mts — the full cloud loop)`,
1593
+ `//`,
1594
+ `// Regenerated on every compile; do not edit.`,
1595
+ `// @nomos-proof-legs ${legsMarker}`,
1596
+ `import { readFileSync } from "node:fs";`,
1597
+ `import { connect, encodeIntentOfferBytes } from "@githolon/client";`,
1598
+ `import { ${factory}, ${hashConst} } from "./${opts.packageName}.client.ts";`,
1599
+ `void connect; void encodeIntentOfferBytes; void ${factory};`,
1600
+ ``,
1601
+ `const CLOUD = (process.env.NOMOS_CLOUD || "https://nomos.captainapp.co.uk").replace(/\\/+$/, "");`,
1602
+ `const WS = process.env.NOMOS_WS || ${JSON.stringify(slug + "-proof-")} + Math.random().toString(36).slice(2, 8);`,
1603
+ ``,
1604
+ ...v2IdentityAndCleanupPreamble(opts, hashConst, n),
1605
+ ``,
1606
+ `// ${n()}. connect + bind the GENERATED typed client (the relation-bootstrap leg drives it with an actor)`,
1607
+ `const holon = await connect({ cloud: CLOUD, workspace: WS, clientId: "proof-1" });`,
1608
+ `ok(\`web client connected — ${factory} bound (replica \${holon.replica.slice(0, 6)}…)\`);`,
1609
+ ``,
1610
+ // The SAME relation-bootstrap emitter the standalone proof uses — seed authority, then prove the GATED
1611
+ // CREATE is admitted (layers 1-3; a speculative seed-path degrades to a diagnostic, never a false green).
1612
+ ...relationSeedLegLines(rb, factory, n),
1613
+ `console.log(\`\\nALL GREEN — generated proof for "${opts.packageName}": deploy → seed authority → gated create ${rb.gatedDirectiveId} admitted (\${WS})\`);`,
1614
+ ``,
1615
+ ...v2CatchFinally(),
1616
+ ``,
1617
+ );
1618
+ return out.join("\n");
1619
+ }
1620
+
1548
1621
  export function generateTsProof(modules: readonly DomainModule[], opts: TsProofOptions): string {
1549
- // PRIMARY-DOMAIN SELECTION: the live `.mts` drives a STANDALONE create when the package has one (the full
1550
- // deploy → write → query → sync → cloud loop). A BIRTH-ONLY package (every `.creates` is also a `.births()`
1551
- // — the home/estate composable shape) has no standalone create to drive, so the primary becomes the first
1552
- // `.births()` domain and the live `.mts` drives the birth as its primary write. Either way, the marker =
1553
- // the primary domain's FULL legs (so the offline runner + the index never describe different directives).
1622
+ // PRIMARY-DOMAIN SELECTION: the live `.mts` drives a STANDALONE (ungated) create when the package has one
1623
+ // (the full deploy → write → query → sync → cloud loop). Else a BIRTH package (`.births()`) drives the
1624
+ // birth. Else a GATED-CREATE package — whose only creating write `.requires(R)` (co2's estate `createSite`)
1625
+ // — drives the RELATION-BOOTSTRAP leg as its primary write: seed authority, THEN the gated create is
1626
+ // admitted (never a naive first dispatch that the gate would refuse). Either way, the marker = the primary
1627
+ // domain's FULL legs (so the offline runner + the index never describe different directives).
1554
1628
  const hasStandaloneCreate = (m: DomainModule): boolean =>
1555
1629
  (m.directives as Directive<unknown>[]).some((d) => isStandaloneCreate(d));
1556
1630
  const hasBirth = (m: DomainModule): boolean => birthDirectiveOf(m) !== undefined;
1557
- const mod = modules.find(hasStandaloneCreate) ?? modules.find(hasBirth);
1631
+ const isBootstrapProvable = (m: DomainModule): boolean => {
1632
+ try { return discoverRelationBootstrapLeg(m) !== undefined; } catch { return false; }
1633
+ };
1634
+ const mod = modules.find(hasStandaloneCreate) ?? modules.find(hasBirth) ?? modules.find(isBootstrapProvable);
1558
1635
  if (mod === undefined) {
1636
+ // No provable creating write anywhere. If a GATED create exists but its authority can't be seeded, surface
1637
+ // the loud bootstrap diagnostic (never a false green); else the generic "add a .creates" remedy.
1638
+ const gapMod = modules.find((m) => diagnoseRelationBootstrapGap(m) !== undefined);
1639
+ const gap = gapMod !== undefined ? diagnoseRelationBootstrapGap(gapMod) : undefined;
1559
1640
  throw new Error(
1560
- `no .creates directive in any domain — a generated proof needs one creating write (or a .births() birth directive); add a directive(...).creates(...) (test/e2e.mts remains the hand-authored lane)`,
1641
+ gap !== undefined
1642
+ ? `no proof could be synthesized: ${gap}`
1643
+ : `no .creates directive in any domain — a generated proof needs one creating write (or a .births() birth directive); add a directive(...).creates(...) (test/e2e.mts remains the hand-authored lane)`,
1561
1644
  );
1562
1645
  }
1563
1646
  const domain = mod.domain ?? mod.name;
@@ -1565,18 +1648,24 @@ export function generateTsProof(modules: readonly DomainModule[], opts: TsProofO
1565
1648
  const hashConst = tsHashConstName(opts.packageName);
1566
1649
  // THE MARKER (since 0.5.0) + the per-domain index are ONE synthesis (`legsForDomain`) — the proof body, its
1567
1650
  // marker, and the index can never describe different directives. The primary domain's legs carry whichever
1568
- // of standalone / child-birth it has.
1651
+ // of standalone / child-birth / relation-bootstrap it has.
1569
1652
  const primaryLegs = legsForDomain(mod, modules);
1570
1653
  const childBirth = primaryLegs.childBirth;
1571
1654
 
1572
- // The standalone-create lane (when the primary has one). A birth-only primary skips this — its primary
1573
- // write is the child-birth leg below.
1574
- const createDir = (mod.directives as Directive<unknown>[]).find(
1575
- (d) => d?.marker === "creates" && typeof d.payloadSchema === "object" && d.birthsChild !== true,
1576
- );
1655
+ // The standalone-create lane needs an UNGATED create (isStandaloneCreate — never a gated/birth/attestation
1656
+ // create, which the gate would refuse dispatched with no seeded authority). Absent one, the primary write
1657
+ // is the birth leg (a `.births()` domain) or the relation-bootstrap leg (a gated-create domain).
1658
+ const createDir = (mod.directives as Directive<unknown>[]).find((d) => isStandaloneCreate(d));
1577
1659
  if (createDir === undefined) {
1578
- // BIRTH-ONLY PRIMARY: emit a birth-shaped proof (no standalone create/query/count/concurrency legs).
1579
- return generateBirthOnlyProof(mod, modules, opts, primaryLegs);
1660
+ // BIRTH-ONLY PRIMARY: emit a birth-shaped proof (the birth replays genesis, seating authority, then the
1661
+ // born child self-validates — a gated child-domain create is proven INSIDE the born child context).
1662
+ if (primaryLegs.childBirth !== undefined) return generateBirthOnlyProof(mod, modules, opts, primaryLegs);
1663
+ // BOOTSTRAP-ONLY PRIMARY: the only creating write is GATED — deploy, then seed authority + prove the
1664
+ // gated create is admitted (the relation-bootstrap leg is its ONLY positive proof).
1665
+ if (primaryLegs.relationBootstrap !== undefined) return generateBootstrapOnlyProof(mod, opts, primaryLegs);
1666
+ // A gated create with no seedable authority: loud diagnostic (compile skips the proof, law still ships).
1667
+ const gap = diagnoseRelationBootstrapGap(mod);
1668
+ throw new Error(gap !== undefined ? `no proof could be synthesized for '${domain}': ${gap}` : `domain '${domain}' has no provable creating write`);
1580
1669
  }
1581
1670
  const agg = (mod.aggregates as AggregateHandle[]).find((a) => a.id === createDir.aggregateId);
1582
1671
  if (agg === undefined) {