@githolon/dsl 0.100.6 → 0.101.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 (82) hide show
  1. package/dart/CHANGELOG.md +6 -0
  2. package/dart/lib/src/dev_cloud.dart +46 -15
  3. package/dart/lib/src/test_harness.dart +78 -16
  4. package/dart/lib/src/webview_bridge.dart +640 -126
  5. package/dart/pubspec.lock +1 -1
  6. package/dart/pubspec.yaml +2 -2
  7. package/dart/test/bundle_version_pairing_test.dart +11 -22
  8. package/dart/test/co2_product_gauntlet_test.dart +228 -0
  9. package/dart/test/errors_test.dart +63 -12
  10. package/dart/test/first_app_gauntlet_test.dart +29 -3
  11. package/dart/test/webview_bridge_test.dart +274 -0
  12. package/dart_types/CHANGELOG.md +6 -0
  13. package/dart_types/lib/src/errors.dart +136 -30
  14. package/dart_types/lib/src/surfaced.dart +48 -2
  15. package/dart_types/lib/src/wire.dart +3 -1
  16. package/dart_types/pubspec.yaml +1 -1
  17. package/dart_types/test/surfaced_test.dart +19 -0
  18. package/package.json +1 -1
  19. package/src/activity.ts +43 -4
  20. package/src/aggregate.ts +132 -22
  21. package/src/attested_core.ts +1 -1
  22. package/src/authoring.ts +119 -6
  23. package/src/birth_meter.ts +4 -4
  24. package/src/birth_meter_ensure.ts +9 -1
  25. package/src/births.ts +53 -22
  26. package/src/build_package.ts +55 -14
  27. package/src/business_birth.ts +48 -7
  28. package/src/codegen_dart.ts +267 -37
  29. package/src/codegen_dart_client.ts +921 -82
  30. package/src/codegen_openapi.ts +28 -5
  31. package/src/codegen_proof.ts +107 -11
  32. package/src/codegen_ts.ts +961 -135
  33. package/src/compile_package_main.ts +155 -45
  34. package/src/conflicts.ts +252 -0
  35. package/src/describe_model.ts +448 -0
  36. package/src/directive.ts +5 -38
  37. package/src/drivers.ts +12 -4
  38. package/src/engine_entry.ts +4 -0
  39. package/src/evolve_preview.ts +72 -11
  40. package/src/fields.ts +201 -55
  41. package/src/framework/application_home.ts +103 -0
  42. package/src/framework/application_home_domain.ts +2 -0
  43. package/src/framework/attested_receipts.ts +2 -2
  44. package/src/framework/auth_provider_law.ts +51 -24
  45. package/src/framework/business_corrections.ts +170 -0
  46. package/src/framework/disclosure.ts +1 -1
  47. package/src/framework/domain_lifecycle.ts +250 -16
  48. package/src/framework/encryption_identity.ts +1 -1
  49. package/src/framework/encryption_scope.ts +21 -13
  50. package/src/framework/era_certification.ts +1 -1
  51. package/src/framework/framework_aggregates.ts +2 -0
  52. package/src/framework/genesis.ts +1 -1
  53. package/src/framework/home.ts +22 -9
  54. package/src/framework/identity.ts +5 -5
  55. package/src/framework/impure_capability.ts +2 -2
  56. package/src/framework/owned_custody.ts +561 -0
  57. package/src/framework/platform_admin.ts +1 -1
  58. package/src/framework/relations.ts +9 -1
  59. package/src/framework/repair.ts +1 -1
  60. package/src/framework/signing_identity.ts +2 -2
  61. package/src/framework/source_roster.ts +1 -1
  62. package/src/framework/workspaces.ts +17 -10
  63. package/src/generated_client_names.ts +39 -0
  64. package/src/index.ts +20 -15
  65. package/src/kernel_capabilities.ts +25 -1
  66. package/src/law_check.ts +4 -1
  67. package/src/manifest.ts +356 -63
  68. package/src/read_projection_manifest.ts +8 -0
  69. package/src/read_projection_namespace.ts +4 -0
  70. package/src/relation.ts +1 -1
  71. package/src/shape_compat.ts +47 -2
  72. package/src/stable_ids.ts +38 -6
  73. package/src/stable_ids_types.ts +30 -0
  74. package/src/surfaced_lint.ts +0 -0
  75. package/src/usd.ts +62 -11
  76. package/src/usd_layers.ts +54 -3
  77. package/src/value_object.ts +23 -19
  78. package/src/wire.ts +1 -0
  79. package/src/wire_encode.ts +48 -4
  80. package/src/workspace_routing.ts +57 -0
  81. package/src/workspace_sharding.ts +9 -9
  82. package/src/certified_read.ts +0 -313
@@ -44,6 +44,13 @@ import type { Field } from "./fields.js";
44
44
  type AnyDirective = Directive<unknown>;
45
45
 
46
46
  const GROUP_PARAM = { name: "group", in: "query", required: false, schema: { type: "string" } } as const;
47
+ const AGGREGATE_TYPE_PARAM = {
48
+ name: "aggregateType",
49
+ in: "query",
50
+ required: false,
51
+ schema: { type: "string" },
52
+ description: "Framework-generated aggregate selector",
53
+ } as const;
47
54
 
48
55
  /** Minimal OpenAPI 3.1 document shape we emit (open object — extensions ride as `x-…` keys). */
49
56
  export interface OpenApiDoc {
@@ -213,8 +220,8 @@ function generateOpenApiSurface(
213
220
  // read — the maintained "decisions needed" index (rows: {aggregateType, aggregateId, field,
214
221
  // observations}). DECLARED BY THE LAW ITSELF (the era-key idiom, like nomosInvariantGate):
215
222
  // emitted iff a Surfaced field is declared, so a Surfaced-free domain's fragment — and thus
216
- // its openapi/package bytes — is byte-identical to before this existed. No params: the read
217
- // is workspace-wide (the reserved id IS the selector).
223
+ // its openapi/package bytes — is byte-identical to before this existed. The generated client
224
+ // supplies aggregateType; no tenant code handles the heterogeneous workspace index.
218
225
  const declaresSurfaced = module.aggregates.some((a) =>
219
226
  Object.values((a.fields ?? {}) as Record<string, Field>).some(
220
227
  (f) => (f.driver as { kind?: string } | undefined)?.kind === "Surfaced",
@@ -223,8 +230,14 @@ function generateOpenApiSurface(
223
230
  if (declaresSurfaced) {
224
231
  getRead(
225
232
  "nomosPendingConflicts",
226
- "pending Surfaced merge conflicts across the workspace (maintained; derived, not drained)",
233
+ "pending Surfaced merge conflicts for one generated aggregate (maintained; derived, not drained)",
227
234
  { kind: "pendingConflicts" },
235
+ [AGGREGATE_TYPE_PARAM],
236
+ );
237
+ getRead(
238
+ "nomosConflictSummary",
239
+ "workspace pending-conflict totals and aggregate breakdown",
240
+ { kind: "pendingConflictSummary" },
228
241
  [],
229
242
  );
230
243
  }
@@ -260,6 +273,14 @@ export function generateOpenApiFragment(
260
273
  ...(module.queries === undefined ? {} : { queries: module.queries.filter((q) => !isInternal(q)) }),
261
274
  };
262
275
  const publicDoc = generateOpenApiSurface(publicModule, opts);
276
+ // Manual conflict escalation is a BUSINESS capability exposed by the generated client, but
277
+ // its maintained workspace index is Nomos plumbing. Keep the reserved selector routable for
278
+ // that client without advertising `/nomosPendingConflicts` as tenant-authored business API.
279
+ const pendingConflictRoutes = ["/nomosPendingConflicts", "/nomosConflictSummary"] as const;
280
+ const frameworkConflictRoutes = Object.fromEntries(
281
+ pendingConflictRoutes.flatMap((path) => publicDoc.paths[path] === undefined ? [] : [[path, publicDoc.paths[path]]]),
282
+ );
283
+ for (const path of pendingConflictRoutes) delete publicDoc.paths[path];
263
284
  if (opts.includeFrameworkRoutes !== true) return publicDoc;
264
285
 
265
286
  const frameworkModule: DomainModule = {
@@ -276,10 +297,12 @@ export function generateOpenApiFragment(
276
297
  spatials: [],
277
298
  };
278
299
  const frameworkDoc = generateOpenApiSurface(frameworkModule, opts);
279
- if (Object.keys(frameworkDoc.paths).length === 0) return publicDoc;
300
+ const frameworkPaths = { ...frameworkDoc.paths };
301
+ Object.assign(frameworkPaths, frameworkConflictRoutes);
302
+ if (Object.keys(frameworkPaths).length === 0) return publicDoc;
280
303
  return {
281
304
  ...publicDoc,
282
- "x-nomos-framework-routes": { paths: frameworkDoc.paths },
305
+ "x-nomos-framework-routes": { paths: frameworkPaths },
283
306
  };
284
307
  }
285
308
 
@@ -34,7 +34,7 @@
34
34
  */
35
35
  import type { z } from "zod";
36
36
 
37
- import type { AggregateHandle } from "./aggregate.js";
37
+ import { BUSINESS_BIRTH_SEED_PAYLOAD_FIELD, type AggregateHandle } from "./aggregate.js";
38
38
  import type { Field } from "./fields.js";
39
39
  import type { Directive } from "./directive.js";
40
40
  import type { DomainModule } from "./codegen_dart.js";
@@ -44,6 +44,7 @@ import type { RelationTypeDecl, Rewrite } from "./relation_schema.js";
44
44
  import type { WorkspaceInvariantDecl } from "./framework/workspace_invariant.js";
45
45
  import { finishCount } from "./count.js";
46
46
  import { clearDirectiveId } from "./clear_directive.js";
47
+ import type { DomainInterface } from "./shape_compat.js";
47
48
  import { voMetaOf, voSample } from "./value_object.js";
48
49
  import { mintedCreateField } from "./workspace_routing.js";
49
50
  import { deterministicPorts } from "./ctx.js";
@@ -1722,6 +1723,14 @@ export interface ChildBirthLeg {
1722
1723
  /** Present for the one application-facing `birth(Aggregate, spec)` declaration. Proof renderers use the
1723
1724
  * business noun and keep the lowered framework ceremony out of normal output. */
1724
1725
  readonly businessAggregateId?: string;
1726
+ /** Hidden data the offline product harness needs to perform the same child-addressed sealing as the
1727
+ * generated client. Present only for an encrypted `.owned()` business birth. */
1728
+ readonly businessEncryption?: {
1729
+ readonly scope: string;
1730
+ readonly namespace: string;
1731
+ readonly keyField: string;
1732
+ readonly fields: Readonly<Record<string, { readonly aggSid: string; readonly fieldSid: string }>>;
1733
+ };
1725
1734
  /** The domain the `.births()` directive lives in. */
1726
1735
  readonly parentDomain: string;
1727
1736
  /** The `.births()` directive id the parent drives to birth the child. */
@@ -1939,6 +1948,15 @@ export function discoverChildBirthLegOrReason(
1939
1948
  // SHAPE is read).
1940
1949
  const probePayload: Record<string, unknown> = { ...synth.value };
1941
1950
  for (const f of synth.autoStamped) probePayload[f] = "2026-01-01T00:00:00Z";
1951
+ const businessBirth = (birthDir as unknown as {
1952
+ businessBirth?: { aggregateId?: string; publicPayloadSchema?: z.ZodTypeAny };
1953
+ }).businessBirth;
1954
+ if (businessBirth !== undefined) {
1955
+ // The real generated client fills this opaque carrier with locally sealed genesis facts. Discovery only
1956
+ // needs the recipe's shape, so an empty JSON object preserves the synthesized plaintext facts while
1957
+ // exercising the same parsing branch without inventing a key inside the compiler.
1958
+ probePayload[BUSINESS_BIRTH_SEED_PAYLOAD_FIELD] = "{}";
1959
+ }
1942
1960
  try {
1943
1961
  withBirthProbeGlobals(() => {
1944
1962
  __resetAuthoring();
@@ -2019,17 +2037,24 @@ export function discoverChildBirthLegOrReason(
2019
2037
  // The parent payload the leg dispatches: the synthesized values MINUS the framework/law-hash placeholders
2020
2038
  // (the runtime injects the REAL hashes onto those fields by name — a `sample-*` hash would fail the child
2021
2039
  // install) MINUS the auto-stamped fields (stamped at dispatch).
2022
- const birthPayload: Record<string, unknown> = { ...synth.value };
2040
+ const publicSynth = businessBirth?.publicPayloadSchema === undefined
2041
+ ? undefined
2042
+ : synthesizePayload(businessBirth.publicPayloadSchema, `${parentDomain}/${birthDir.id} public birth`);
2043
+ const birthPayload: Record<string, unknown> = {
2044
+ ...(publicSynth?.value ?? synth.value),
2045
+ };
2023
2046
  if (frameworkHashField !== undefined) delete birthPayload[frameworkHashField];
2024
2047
  if (lawHashField !== undefined) delete birthPayload[lawHashField];
2025
2048
 
2026
- const businessAggregateId = (birthDir as unknown as { businessBirth?: { aggregateId?: string } }).businessBirth?.aggregateId;
2049
+ const businessAggregateId = businessBirth?.aggregateId;
2027
2050
  const leg: ChildBirthLeg = {
2028
2051
  parentDomain,
2029
2052
  birthDirectiveId: birthDir.id,
2030
2053
  ...(businessAggregateId !== undefined ? { businessAggregateId } : {}),
2031
2054
  birthPayload,
2032
- birthAutoStamped: synth.autoStamped,
2055
+ birthAutoStamped: businessBirth !== undefined
2056
+ ? [...new Set([...(publicSynth?.autoStamped ?? []), "bornAt"])]
2057
+ : synth.autoStamped,
2033
2058
  ...(actorField !== undefined ? { birthActorField: actorField } : {}),
2034
2059
  ...(birthDir.requiresRelationGate !== undefined ? { birthRequiresRelation: birthDir.requiresRelationGate.relation } : {}),
2035
2060
  ...(frameworkHashField !== undefined ? { frameworkHashField } : {}),
@@ -2140,6 +2165,8 @@ export interface TsProofOptions {
2140
2165
  readonly packageName: string;
2141
2166
  /** sha256 of the built `.package.usda` — checked against the deploy response. */
2142
2167
  readonly domainHash: string;
2168
+ /** Stable-id structural interface used to teach the offline harness the hidden owned-birth seal slots. */
2169
+ readonly interface?: Readonly<Record<string, DomainInterface>>;
2143
2170
  }
2144
2171
 
2145
2172
  /** THE PROOF TAXONOMY (co2 feedback): which legs a domain carries.
@@ -2225,6 +2252,10 @@ export interface ProofLegs {
2225
2252
  * domain proves via its bootstrap / child-birth / compile-valid legs instead. */
2226
2253
  function isStandaloneCreate(d: Directive<unknown> | undefined): boolean {
2227
2254
  return d?.marker === "creates" && typeof d.payloadSchema === "object"
2255
+ // Framework lifecycle directives are installed in the certified law so the generated client can
2256
+ // drive them behind its managed methods, but they are not tenant vocabulary. Selecting one here
2257
+ // makes the generated proof call a method that the public generated client correctly does not expose.
2258
+ && (d as { businessBirthInternal?: unknown }).businessBirthInternal !== true
2228
2259
  && d.birthsChild !== true && d.attestationAuthorizedGate === undefined
2229
2260
  && d.requiresRelationGate === undefined
2230
2261
  // NOT a foreign-read directive (`.readsFrom(...)` — a static attested edge or a dynamic
@@ -2346,10 +2377,60 @@ function hasBirthDirective(mod: DomainModule): boolean {
2346
2377
  return (mod.directives as Directive<unknown>[]).some((d) => d?.birthsChild === true && typeof d.payloadSchema === "object");
2347
2378
  }
2348
2379
 
2380
+ function attachBusinessEncryption(
2381
+ leg: ChildBirthLeg,
2382
+ modules: readonly DomainModule[],
2383
+ packageInterface: Readonly<Record<string, DomainInterface>> | undefined,
2384
+ ): ChildBirthLeg {
2385
+ if (leg.businessAggregateId === undefined || packageInterface === undefined) return leg;
2386
+ const mod = modules.find((candidate) => (candidate.domain ?? candidate.name) === leg.parentDomain);
2387
+ const aggregate = mod?.aggregates.find((candidate) => candidate.id === leg.businessAggregateId);
2388
+ if (aggregate?.audience?.kind !== "owned" || (aggregate.encryptedFields?.length ?? 0) === 0) return leg;
2389
+ const directive = (mod?.directives as Directive<unknown>[] | undefined)?.find(
2390
+ (candidate) => candidate.id === leg.birthDirectiveId,
2391
+ ) as (Directive<unknown> & {
2392
+ businessBirth?: { namespace?: string; keyField?: string };
2393
+ }) | undefined;
2394
+ const namespace = directive?.businessBirth?.namespace;
2395
+ const keyField = directive?.businessBirth?.keyField;
2396
+ const offered = packageInterface[leg.parentDomain];
2397
+ const offeredAggregate = offered === undefined
2398
+ ? undefined
2399
+ : Object.values(offered.aggregates).find((candidate) => candidate.label === leg.businessAggregateId);
2400
+ if (namespace === undefined || keyField === undefined || offeredAggregate === undefined || aggregate.visibility === undefined) {
2401
+ throw new Error(
2402
+ `${leg.parentDomain}/${leg.birthDirectiveId}: Nomos could not derive the hidden owned-birth custody map`,
2403
+ );
2404
+ }
2405
+ const fields: Record<string, { aggSid: string; fieldSid: string }> = {};
2406
+ for (const field of aggregate.encryptedFields ?? []) {
2407
+ const slot = Object.entries(offeredAggregate.fields).find(([, candidate]) => candidate.label === field);
2408
+ if (slot === undefined) {
2409
+ throw new Error(
2410
+ `${leg.parentDomain}/${leg.birthDirectiveId}: owned birth field '${field}' has no stable interface slot`,
2411
+ );
2412
+ }
2413
+ fields[field] = { aggSid: offeredAggregate.sid, fieldSid: slot[0] };
2414
+ }
2415
+ return {
2416
+ ...leg,
2417
+ businessEncryption: {
2418
+ scope: aggregate.visibility.requires,
2419
+ namespace,
2420
+ keyField,
2421
+ fields,
2422
+ },
2423
+ };
2424
+ }
2425
+
2349
2426
  /** Build the legs for ONE domain module — every leg it carries (compile-valid always; standalone-provable
2350
2427
  * when it has a `.creates`; child-birth-provable when it has a `.births()`). NEVER null: every compiled
2351
2428
  * domain is at least `compile-valid` (gaps 1+3 — every domain is proof-addressable). */
2352
- export function legsForDomain(mod: DomainModule, modules: readonly DomainModule[]): ProofLegs {
2429
+ export function legsForDomain(
2430
+ mod: DomainModule,
2431
+ modules: readonly DomainModule[],
2432
+ packageInterface?: Readonly<Record<string, DomainInterface>>,
2433
+ ): ProofLegs {
2353
2434
  // Business-birth control declarations are proofed as part of the birth's child genesis, not as
2354
2435
  // tenant-authored standalone/gated actions. Keep the full `modules` below for recipe resolution while
2355
2436
  // removing the presentation-only internals from leg selection and diagnostics.
@@ -2399,7 +2480,7 @@ export function legsForDomain(mod: DomainModule, modules: readonly DomainModule[
2399
2480
  if (rb !== undefined && rb.bornContext === undefined) return { ...withParent, relationSetup: rb };
2400
2481
  }
2401
2482
  return withParent;
2402
- });
2483
+ }).map((birthLeg) => attachBusinessEncryption(birthLeg, modules, packageInterface));
2403
2484
  const childBirthWithSetup = childBirths[0];
2404
2485
 
2405
2486
  // The relation-bootstrap leg (when the domain has a `.requires(R)`-gated directive AND a discoverable
@@ -2497,11 +2578,14 @@ export function legsForDomain(mod: DomainModule, modules: readonly DomainModule[
2497
2578
  * domain key pointing at the parent birth that proves it. `githolon proof [--domain <key>]` resolves for
2498
2579
  * every domain. The default is the first STANDALONE-provable domain (the live `.mts` lane needs one), else
2499
2580
  * the first child-birth-provable, else the first compiled domain. */
2500
- export function proofLegsIndex(modules: readonly DomainModule[]): { default: string; domains: Record<string, ProofLegs> } | null {
2581
+ export function proofLegsIndex(
2582
+ modules: readonly DomainModule[],
2583
+ packageInterface?: Readonly<Record<string, DomainInterface>>,
2584
+ ): { default: string; domains: Record<string, ProofLegs> } | null {
2501
2585
  const domains: Record<string, ProofLegs> = {};
2502
2586
  const order: string[] = [];
2503
2587
  for (const mod of modules) {
2504
- const legs = legsForDomain(mod, modules);
2588
+ const legs = legsForDomain(mod, modules, packageInterface);
2505
2589
  if (domains[legs.domain] === undefined) order.push(legs.domain);
2506
2590
  domains[legs.domain] = legs;
2507
2591
  }
@@ -2913,7 +2997,14 @@ export function generateTsProof(modules: readonly DomainModule[], opts: TsProofO
2913
2997
  (m.directives as Directive<unknown>[]).some((d) => isStandaloneCreate(d));
2914
2998
  const hasBirth = (m: DomainModule): boolean => hasBirthDirective(m);
2915
2999
  const isBootstrapProvable = (m: DomainModule): boolean => {
2916
- try { return discoverRelationBootstrapLeg(m) !== undefined; } catch { return false; }
3000
+ try {
3001
+ return discoverRelationBootstrapLeg({
3002
+ ...m,
3003
+ directives: (m.directives as Directive<unknown>[]).filter(
3004
+ (d) => (d as { businessBirthInternal?: unknown }).businessBirthInternal !== true,
3005
+ ),
3006
+ }) !== undefined;
3007
+ } catch { return false; }
2917
3008
  };
2918
3009
  const mod = modules.find(hasStandaloneCreate) ?? modules.find(hasBirth) ?? modules.find(isBootstrapProvable);
2919
3010
  if (mod === undefined) {
@@ -2933,7 +3024,7 @@ export function generateTsProof(modules: readonly DomainModule[], opts: TsProofO
2933
3024
  // THE MARKER (since 0.5.0) + the per-domain index are ONE synthesis (`legsForDomain`) — the proof body, its
2934
3025
  // marker, and the index can never describe different directives. The primary domain's legs carry whichever
2935
3026
  // of standalone / child-birth / relation-bootstrap it has.
2936
- const primaryLegs = legsForDomain(mod, modules);
3027
+ const primaryLegs = legsForDomain(mod, modules, opts.interface);
2937
3028
  const childBirth = primaryLegs.childBirth;
2938
3029
 
2939
3030
  // The standalone-create lane needs an UNGATED create (isStandaloneCreate — never a gated/birth/attestation
@@ -2993,7 +3084,12 @@ export function generateTsProof(modules: readonly DomainModule[], opts: TsProofO
2993
3084
  // THE AUTHORITY-CONFORMANCE LEG (the one-encoder + gate proof): the first gated (`.requires(...)`) directive
2994
3085
  // in the primary domain, proven locally — actor threads through the one encoder; an actorless gated offer
2995
3086
  // throws MissingOfferAuthority. `undefined` ⇒ a documented no-op (the domain authors openly).
2996
- const conformance = discoverAuthorityConformanceLeg(mod);
3087
+ const conformance = discoverAuthorityConformanceLeg({
3088
+ ...mod,
3089
+ directives: (mod.directives as Directive<unknown>[]).filter(
3090
+ (d) => (d as { businessBirthInternal?: unknown }).businessBirthInternal !== true,
3091
+ ),
3092
+ });
2997
3093
 
2998
3094
  // THE RELATION-BOOTSTRAP LEG (the seed-then-prove positive): grant relation R to the proof actor, then prove
2999
3095
  // a `.requires(R)`-gated write SUCCEEDS under that actor. `undefined` ⇒ no gated directive or no discoverable