@githolon/dsl 0.102.4 → 0.103.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.
Files changed (131) hide show
  1. package/compile_package.mjs +43 -2
  2. package/compile_proof.d.mts +47 -0
  3. package/compile_proof.mjs +343 -0
  4. package/dart/CHANGELOG.md +7 -0
  5. package/dart/lib/src/dev_cloud.dart +8 -8
  6. package/dart/lib/src/nms_generated_runtime.dart +2 -0
  7. package/dart/lib/src/runner/node-entry.mjs +5504 -1236
  8. package/dart/lib/src/runner/offline-install.mjs +4578 -528
  9. package/dart/lib/src/test_harness.dart +206 -152
  10. package/dart/lib/src/webview_bridge.dart +196 -89
  11. package/dart/pubspec.lock +1 -1
  12. package/dart/pubspec.yaml +2 -2
  13. package/dart/test/acquire_session_credentials_test.dart +4 -3
  14. package/dart/test/adv_realm_interference_test.dart +7 -6
  15. package/dart/test/cloud_bound_topology_test.dart +3 -3
  16. package/dart/test/cluster_births_test.dart +4 -3
  17. package/dart/test/cluster_device_keyed_acceptance_test.dart +10 -16
  18. package/dart/test/cluster_local_autosync_test.dart +3 -2
  19. package/dart/test/cluster_scale_test.dart +3 -2
  20. package/dart/test/dlq_session_surface_test.dart +7 -6
  21. package/dart/test/first_app_gauntlet_test.dart +14 -21
  22. package/dart/test/fixtures/wedge/fake-install.mjs +1 -1
  23. package/dart/test/four_layer_nested_topology_test.dart +4 -4
  24. package/dart/test/harness_phase_timeout_test.dart +2 -2
  25. package/dart/test/local_sync_inert_test.dart +1 -1
  26. package/dart/test/parent_signer_recovery_test.dart +2 -2
  27. package/dart/test/sealing_client_test.dart +4 -3
  28. package/dart/test/session_bound_test.dart +8 -7
  29. package/dart/test/test_harness_test.dart +13 -12
  30. package/dart/test/vo_read_typing_test.dart +4 -3
  31. package/dart/test/watch_after_offer_stall_test.dart +4 -3
  32. package/dart/test/webview_bridge_test.dart +102 -5
  33. package/dart_types/CHANGELOG.md +5 -0
  34. package/dart_types/lib/nomos_types.dart +9 -0
  35. package/dart_types/lib/src/nomos_ref.dart +20 -0
  36. package/dart_types/lib/src/optional_ref.dart +85 -0
  37. package/dart_types/lib/src/sha256_digest.dart +41 -0
  38. package/dart_types/lib/src/versioned_nomos_ref.dart +81 -0
  39. package/dart_types/pubspec.yaml +1 -1
  40. package/dart_types/test/optional_ref_test.dart +44 -0
  41. package/dart_types/test/sha256_digest_test.dart +18 -0
  42. package/package.json +9 -5
  43. package/runtime/generated-application-host.js +5365 -1105
  44. package/src/aggregate.ts +18 -0
  45. package/src/application_composition.ts +6 -32
  46. package/src/attested_core.ts +6 -3
  47. package/src/birth_meter.ts +1 -0
  48. package/src/birth_meter_ensure.ts +28 -2
  49. package/src/births.ts +1 -1
  50. package/src/build_package.ts +162 -538
  51. package/src/business_birth.ts +19 -3
  52. package/src/canonical_json.ts +21 -0
  53. package/src/check_package_main.ts +76 -35
  54. package/src/clear_directive.ts +19 -8
  55. package/src/codegen_dart.ts +305 -22
  56. package/src/codegen_dart_application.ts +844 -292
  57. package/src/codegen_dart_client.ts +1084 -407
  58. package/src/codegen_dart_scenario.ts +12 -12
  59. package/src/codegen_proof.ts +169 -39
  60. package/src/codegen_provider_dart.ts +17 -1
  61. package/src/codegen_ts.ts +298 -106
  62. package/src/compat_manifest.ts +2 -2
  63. package/src/compat_package_main.ts +8 -12
  64. package/src/compile_package_main.ts +1866 -597
  65. package/src/compose.ts +1 -1
  66. package/src/directive.ts +208 -15
  67. package/src/engine_entry.ts +165 -111
  68. package/src/evolve_preview.ts +43 -81
  69. package/src/extremum.ts +1 -1
  70. package/src/fields.ts +183 -4
  71. package/src/flow_gate.ts +75 -2
  72. package/src/framework/application_home.ts +2 -0
  73. package/src/framework/auth_provider_law.ts +2 -2
  74. package/src/framework/birth_meter.ts +7 -19
  75. package/src/framework/bootstrap.ts +0 -2
  76. package/src/framework/business_corrections.ts +9 -5
  77. package/src/framework/domain_lifecycle.ts +15 -29
  78. package/src/framework/encryption_scope.ts +2 -1
  79. package/src/framework/framework_aggregates.ts +2 -0
  80. package/src/framework/genesis.ts +16 -14
  81. package/src/framework/home.ts +20 -14
  82. package/src/framework/owned_custody.ts +9 -3
  83. package/src/framework/platform_admin.ts +3 -3
  84. package/src/framework/relations.ts +21 -13
  85. package/src/framework/signing_identity.ts +45 -40
  86. package/src/framework/workspace_invariant.ts +245 -0
  87. package/src/framework/workspaces.ts +23 -29
  88. package/src/index.ts +36 -16
  89. package/src/intent.ts +178 -6
  90. package/src/law_check.ts +32 -11
  91. package/src/law_graph.ts +2902 -0
  92. package/src/law_graph_usda.ts +1799 -0
  93. package/src/law_identity.ts +435 -0
  94. package/src/law_value_type.ts +230 -0
  95. package/src/lsp_server.ts +61 -31
  96. package/src/ops.ts +7 -0
  97. package/src/package_config.ts +141 -0
  98. package/src/package_dependencies.ts +108 -0
  99. package/src/package_dependency_usage.ts +150 -0
  100. package/src/package_source_imports.ts +71 -0
  101. package/src/plan_source_slice.ts +335 -0
  102. package/src/point_guard.ts +192 -0
  103. package/src/precondition.ts +44 -9
  104. package/src/read_projection_namespace.ts +1 -1
  105. package/src/runtime_zod.ts +44 -0
  106. package/src/secondary_ensure.ts +67 -14
  107. package/src/shape_compat.ts +1 -1
  108. package/src/stable_id_identity_bridge.ts +149 -0
  109. package/src/stable_id_lockfile.ts +278 -0
  110. package/src/stable_ids.ts +7 -2
  111. package/src/stable_ids_for_module.ts +348 -0
  112. package/src/stable_ids_types.ts +1 -1
  113. package/src/sum.ts +1 -1
  114. package/src/usd_state.ts +1 -1
  115. package/src/usda_relationship_integrity.ts +66 -0
  116. package/src/usda_text.ts +18 -0
  117. package/src/usdz.ts +78 -0
  118. package/src/value_object.ts +95 -0
  119. package/src/wire.ts +19 -0
  120. package/src/wire_encode.ts +22 -0
  121. package/src/workspace_sharding.ts +52 -36
  122. package/src/workspace_sharing.ts +28 -4
  123. package/src/zod_introspection.ts +1 -1
  124. package/dart/test/cluster_attested_test.dart +0 -201
  125. package/src/compile_engine.ts +0 -595
  126. package/src/manifest.ts +0 -1476
  127. package/src/read_projection_composition.ts +0 -117
  128. package/src/read_projection_manifest.ts +0 -115
  129. package/src/relation_composition.ts +0 -286
  130. package/src/usd.ts +0 -1967
  131. package/src/usd_layers.ts +0 -1776
@@ -49,7 +49,7 @@ import { directive } from "../directive.js";
49
49
  import { set, withMarker, type PlannedOp } from "../ops.js";
50
50
  import { query } from "../query.js";
51
51
  import { relationType, self, key } from "../relation_schema.js";
52
- import { workspaceInvariant, ref } from "./workspace_invariant.js";
52
+ import { readRef, readSet, workspaceInvariant, ref } from "./workspace_invariant.js";
53
53
  import { endorsementOf, type EndorsedAuthority } from "../principal_evidence.js";
54
54
  import {
55
55
  writeTuple,
@@ -144,7 +144,7 @@ export const advanceSignerEpoch = directive("advanceSignerEpoch")
144
144
  */
145
145
  export const signerEpochStrictlyIncreases = workspaceInvariant("SignerEpochStrictlyIncreases")
146
146
  .on("advanceSignerEpoch")
147
- .reads(() => [ref("SignerEpoch", SIGNER_EPOCH_ID)])
147
+ .reads(readSet(readRef("signerEpoch", SignerEpoch).at(SIGNER_EPOCH_ID)))
148
148
  .assert((snap, ctx) => {
149
149
  const applied = snap["signerEpoch"] as { currentEpoch?: number } | undefined;
150
150
  const declared = Number((ctx?.intent as { epoch?: number } | undefined)?.epoch ?? NaN);
@@ -166,7 +166,7 @@ export const signerEpochStrictlyIncreases = workspaceInvariant("SignerEpochStric
166
166
  */
167
167
  export const revokeSignerEpochStrictlyIncreases = workspaceInvariant("RevokeSignerEpochStrictlyIncreases")
168
168
  .on("revokeSigner")
169
- .reads(() => [ref("SignerEpoch", SIGNER_EPOCH_ID)])
169
+ .reads(readSet(readRef("signerEpoch", SignerEpoch).at(SIGNER_EPOCH_ID)))
170
170
  .assert((snap, ctx) => {
171
171
  const applied = snap["signerEpoch"] as { currentEpoch?: number } | undefined;
172
172
  const declared = Number((ctx?.intent as { newEpoch?: number } | undefined)?.newEpoch ?? NaN);
@@ -289,7 +289,7 @@ export const enrollSignerSelf = directive("enrollSignerSelf")
289
289
  */
290
290
  export const enrollSignerSelfRequiresVerified = workspaceInvariant("EnrollSignerSelfRequiresVerified")
291
291
  .on("enrollSignerSelf")
292
- .reads(() => []) // no aggregate snapshot needed — the verdict is over the captured principal-verified flag
292
+ .reads(readSet()) // no aggregate snapshot needed — the verdict is over the captured principal-verified flag
293
293
  .declassify("the self-enroll gate intentionally branches on principal_verified (an authz fact, not a secret read)")
294
294
  .assert((_snap, ctx) =>
295
295
  ctx?.principalVerified === true
@@ -297,6 +297,44 @@ export const enrollSignerSelfRequiresVerified = workspaceInvariant("EnrollSigner
297
297
  : { reject: "enroll-signer-self-requires-verified-principal" },
298
298
  );
299
299
 
300
+ // ─────────────────── THE ENROLL-ONCE DELEGATION CERT (the peer-sovereign root-of-trust) ───────────────────
301
+
302
+ /**
303
+ * THE SIGNER DELEGATION (the enroll-once delegation cert, `executor::birth_cert`). When the TENANT verifies a
304
+ * user U's IdP token at U's live enroll gate, the tenant's onboarding signer `S_P` signs — ONCE — a
305
+ * DELEGATION CERT binding U's device key `P_U` to the tenant's lineage: a `BirthCert {founder: U,
306
+ * parent_key: P_U, …}` signed by `S_P`. This singleton (keyed by U) records it in the PLATFORM's folded
307
+ * state, so the kernel's birth offer-effect reads U's delegation by-user (trusted, not from the payload) when
308
+ * it assembles U's home cert chain. The cert is OPAQUE on-chain (a JSON `BirthCert`); the kernel parses +
309
+ * verifies it as one chain link (signed by S_P, whose pubkey is attested by the platform's own chain).
310
+ *
311
+ * SELF-BOUND / control-plane: enrolled by the tenant onboarding lane (composed into the tenant's governance
312
+ * law, off the open session lane), so a tenant cannot forge a delegation for a user another tenant verified.
313
+ * The delegation's `founder` IS the gate's binding — the kernel asserts the home cert's founder equals the
314
+ * delegation's founder (the crux founder-binding), so a delegation enrolled for A cannot mint a home for V.
315
+ *
316
+ * Declared before `enrollFirstDevice` because that directive's typed effect signature and optional plan arm
317
+ * both refer to this aggregate during module initialisation.
318
+ */
319
+ export const SIGNER_DELEGATION_PREFIX = "signer-delegation:";
320
+ export const SignerDelegation = aggregate("SignerDelegation", {
321
+ /** The user (a `user:<uid>` subject) this delegation enrolls — the key of the singleton. */
322
+ user: t.string().merge(Lww),
323
+ /** The user's device PUBLIC KEY (base64) the delegation binds — `P_U`, the home_cert's signer. */
324
+ deviceKey: t.string().merge(Lww),
325
+ /** THE DELEGATION CERT (JSON `executor::birth_cert::BirthCert`), signed by the tenant's `S_P`. Opaque
326
+ * on-chain; the kernel parses + verifies it as the `links[1]` delegation in U's travelling chain. */
327
+ certJson: t.string().merge(Lww),
328
+ /** ISO-8601, caller-stamped (determinism — no clock in the plan). */
329
+ enrolledAt: t.string().merge(Lww),
330
+ }).public();
331
+ export type SignerDelegationHandle = typeof SignerDelegation;
332
+
333
+ /** The deterministic singleton id of a user's signer delegation (the kernel reads THIS id by-user). */
334
+ export function signerDelegationInstanceId(user: string): string {
335
+ return SIGNER_DELEGATION_PREFIX + user;
336
+ }
337
+
300
338
  /**
301
339
  * `enrollFirstDevice` (architecture/keyless_verified_onboard.md) — THE KEYLESS VERIFIED FIRST-DEVICE
302
340
  * self-onboard. A brand-new principal (an IdP-verified user with NO enrolled signer key) bootstraps its
@@ -377,6 +415,7 @@ export const enrollFirstDevice = directive("enrollFirstDevice")
377
415
  ];
378
416
  return [...tupleOps, ...delegationOps];
379
417
  })
418
+ .alsoEnsures(SignerDelegation)
380
419
  // THE KEYLESS GATE DECLARATION: the carried device-bound `idpToken` IS this directive's authorization
381
420
  // (re-verified against the workspace AuthProvider on every lane), not a pre-enrolled signer. This lowers
382
421
  // the per-directive payload FIELD MAPPING into the `nomosVerifiedFirstEnroll` era key, so the KERNEL reads
@@ -417,42 +456,8 @@ export const revokeSigner = directive("revokeSigner")
417
456
  // bump the signer epoch so the revoked key ages out (the live-epoch backdate is phase 4).
418
457
  ...writeSignerEpoch(p.newEpoch),
419
458
  ];
420
- });
421
-
422
- // ─────────────────── THE ENROLL-ONCE DELEGATION CERT (the peer-sovereign root-of-trust) ───────────────────
423
-
424
- /**
425
- * THE SIGNER DELEGATION (the enroll-once delegation cert, `executor::birth_cert`). When the TENANT verifies a
426
- * user U's IdP token at U's live enroll gate, the tenant's onboarding signer `S_P` signs — ONCE — a
427
- * DELEGATION CERT binding U's device key `P_U` to the tenant's lineage: a `BirthCert {founder: U,
428
- * parent_key: P_U, …}` signed by `S_P`. This singleton (keyed by U) records it in the PLATFORM's folded
429
- * state, so the kernel's birth offer-effect reads U's delegation by-user (trusted, not from the payload) when
430
- * it assembles U's home cert chain. The cert is OPAQUE on-chain (a JSON `BirthCert`); the kernel parses +
431
- * verifies it as one chain link (signed by S_P, whose pubkey is attested by the platform's own chain).
432
- *
433
- * SELF-BOUND / control-plane: enrolled by the tenant onboarding lane (composed into the tenant's governance
434
- * law, off the open session lane), so a tenant cannot forge a delegation for a user another tenant verified.
435
- * The delegation's `founder` IS the gate's binding — the kernel asserts the home cert's founder equals the
436
- * delegation's founder (the crux founder-binding), so a delegation enrolled for A cannot mint a home for V.
437
- */
438
- export const SIGNER_DELEGATION_PREFIX = "signer-delegation:";
439
- export const SignerDelegation = aggregate("SignerDelegation", {
440
- /** The user (a `user:<uid>` subject) this delegation enrolls — the key of the singleton. */
441
- user: t.string().merge(Lww),
442
- /** The user's device PUBLIC KEY (base64) the delegation binds — `P_U`, the home_cert's signer. */
443
- deviceKey: t.string().merge(Lww),
444
- /** THE DELEGATION CERT (JSON `executor::birth_cert::BirthCert`), signed by the tenant's `S_P`. Opaque
445
- * on-chain; the kernel parses + verifies it as the `links[1]` delegation in U's travelling chain. */
446
- certJson: t.string().merge(Lww),
447
- /** ISO-8601, caller-stamped (determinism — no clock in the plan). */
448
- enrolledAt: t.string().merge(Lww),
449
- }).public();
450
- export type SignerDelegationHandle = typeof SignerDelegation;
451
-
452
- /** The deterministic singleton id of a user's signer delegation (the kernel reads THIS id by-user). */
453
- export function signerDelegationInstanceId(user: string): string {
454
- return SIGNER_DELEGATION_PREFIX + user;
455
- }
459
+ })
460
+ .alsoEnsures(SignerEpoch);
456
461
 
457
462
  /**
458
463
  * `enrollDelegation` — record a user's tenant-signed delegation cert (the enroll-once lane). The tenant's
@@ -35,6 +35,8 @@
35
35
  * workspace scan / projection read / ambient "current world" read — only the declared refs.
36
36
  */
37
37
 
38
+ import type { PointGuardSpec } from "../point_guard.js";
39
+
38
40
  /** A TYPED verdict — identical shape to the aggregate invariant (the engine parses both). */
39
41
  export type WorkspaceInvariantVerdict = { accept: true } | { reject: string };
40
42
 
@@ -51,6 +53,240 @@ export interface InvariantRef {
51
53
  readonly id: string;
52
54
  }
53
55
 
56
+ /** A native point-read recipe. This is authored in TypeScript, custodied as typed USDA, and
57
+ * interpreted by Rust. It is data, not an executable JavaScript read selector. */
58
+ export type InvariantReadTransform = "none" | "row-key" | "stable-id";
59
+
60
+ export type InvariantReadIdPart =
61
+ | { readonly source: "literal"; readonly value: string }
62
+ | { readonly source: "payload" | "item"; readonly path: readonly string[]; readonly transform: InvariantReadTransform };
63
+
64
+ export type InvariantReadId =
65
+ | { readonly source: "literal"; readonly value: string }
66
+ | {
67
+ readonly source: "payload";
68
+ readonly path: readonly string[];
69
+ readonly prefix: string;
70
+ readonly suffix: string;
71
+ readonly optional: boolean;
72
+ }
73
+ | { readonly source: "template"; readonly parts: readonly InvariantReadIdPart[]; readonly optional: boolean };
74
+
75
+ export interface InvariantReadBinding {
76
+ readonly binding: string;
77
+ readonly aggregate: string;
78
+ readonly state: "pre" | "post";
79
+ readonly eachPayloadPath?: readonly string[];
80
+ readonly id: InvariantReadId;
81
+ }
82
+
83
+ function readPath(path: string | readonly string[]): readonly string[] {
84
+ return typeof path === "string" ? path.split(".").filter(Boolean) : [...path];
85
+ }
86
+
87
+ export function idLiteral(value: string): InvariantReadIdPart {
88
+ return { source: "literal", value };
89
+ }
90
+
91
+ export function idPayload(
92
+ path: string | readonly string[],
93
+ transform: InvariantReadTransform = "none",
94
+ ): InvariantReadIdPart {
95
+ return { source: "payload", path: readPath(path), transform };
96
+ }
97
+
98
+ export function idItem(
99
+ path: string | readonly string[] = [],
100
+ transform: InvariantReadTransform = "none",
101
+ ): InvariantReadIdPart {
102
+ return { source: "item", path: readPath(path), transform };
103
+ }
104
+
105
+ export function idTemplate(
106
+ ...parts: readonly (InvariantReadIdPart | string)[]
107
+ ): readonly InvariantReadIdPart[] {
108
+ return parts.map((part) => typeof part === "string" ? idLiteral(part) : part);
109
+ }
110
+
111
+ const NATIVE_READ_SET = Symbol.for("nomos.workspaceInvariant.nativeReadSet.v1");
112
+
113
+ type NativeReadSet = WorkspaceInvariantReads & {
114
+ readonly [NATIVE_READ_SET]: readonly InvariantReadBinding[];
115
+ };
116
+
117
+ function aggregateName(aggregate: { readonly id: string } | string): string {
118
+ return typeof aggregate === "string" ? aggregate : aggregate.id;
119
+ }
120
+
121
+ /** Begin one typed, bounded aggregate read. Prefer an aggregate handle; strings remain available
122
+ * for framework composition where the declaration is intentionally late-bound. */
123
+ export function readRef(
124
+ binding: string,
125
+ aggregate: { readonly id: string } | string,
126
+ ): {
127
+ at(id: string): InvariantReadBinding;
128
+ fromPayload(path: string | readonly string[], options?: { readonly prefix?: string; readonly suffix?: string }): InvariantReadBinding;
129
+ fromOptionalPayload(path: string | readonly string[], options?: { readonly prefix?: string; readonly suffix?: string }): InvariantReadBinding;
130
+ fromTemplate(parts: readonly InvariantReadIdPart[], options?: { readonly optional?: boolean }): InvariantReadBinding;
131
+ forEach(
132
+ path: string | readonly string[],
133
+ parts: readonly InvariantReadIdPart[],
134
+ options?: { readonly optional?: boolean },
135
+ ): InvariantReadBinding;
136
+ pre(): ReturnType<typeof readRef>;
137
+ post(): ReturnType<typeof readRef>;
138
+ } {
139
+ const build = (state: "pre" | "post") => ({
140
+ at(id: string): InvariantReadBinding {
141
+ return { binding, aggregate: aggregateName(aggregate), state, id: { source: "literal", value: id } };
142
+ },
143
+ fromPayload(
144
+ path: string | readonly string[],
145
+ options: { readonly prefix?: string; readonly suffix?: string } = {},
146
+ ): InvariantReadBinding {
147
+ return {
148
+ binding,
149
+ aggregate: aggregateName(aggregate),
150
+ state,
151
+ id: {
152
+ source: "payload",
153
+ path: readPath(path),
154
+ prefix: options.prefix ?? "",
155
+ suffix: options.suffix ?? "",
156
+ optional: false,
157
+ },
158
+ };
159
+ },
160
+ fromOptionalPayload(
161
+ path: string | readonly string[],
162
+ options: { readonly prefix?: string; readonly suffix?: string } = {},
163
+ ): InvariantReadBinding {
164
+ const binding = build(state).fromPayload(path, options);
165
+ if (binding.id.source !== "payload") return binding;
166
+ return { ...binding, id: { ...binding.id, optional: true } };
167
+ },
168
+ fromTemplate(
169
+ parts: readonly InvariantReadIdPart[],
170
+ options: { readonly optional?: boolean } = {},
171
+ ): InvariantReadBinding {
172
+ return {
173
+ binding,
174
+ aggregate: aggregateName(aggregate),
175
+ state,
176
+ id: { source: "template", parts: [...parts], optional: options.optional ?? false },
177
+ };
178
+ },
179
+ forEach(
180
+ path: string | readonly string[],
181
+ parts: readonly InvariantReadIdPart[],
182
+ options: { readonly optional?: boolean } = {},
183
+ ): InvariantReadBinding {
184
+ return {
185
+ ...build(state).fromTemplate(parts, options),
186
+ eachPayloadPath: readPath(path),
187
+ };
188
+ },
189
+ pre: () => build("pre"),
190
+ post: () => build("post"),
191
+ });
192
+ return build("post");
193
+ }
194
+
195
+ function valueAtPath(value: unknown, path: readonly string[]): unknown {
196
+ let current = value;
197
+ for (const part of path) {
198
+ if (current === null || typeof current !== "object") return undefined;
199
+ current = (current as Record<string, unknown>)[part];
200
+ }
201
+ return current;
202
+ }
203
+
204
+ function transformReadPart(value: string, transform: InvariantReadTransform): string {
205
+ if (transform === "row-key") {
206
+ return value.replace(/%/g, "%25").replace(/_/g, "%5F").replace(/\|/g, "%7C");
207
+ }
208
+ if (transform === "stable-id") return value.replace(/[^A-Za-z0-9.@/-]/g, "-");
209
+ return value;
210
+ }
211
+
212
+ function resolveTemplate(
213
+ parts: readonly InvariantReadIdPart[],
214
+ intent: Record<string, unknown>,
215
+ item: unknown,
216
+ ): string | undefined {
217
+ let result = "";
218
+ for (const part of parts) {
219
+ if (part.source === "literal") {
220
+ result += part.value;
221
+ continue;
222
+ }
223
+ const value = valueAtPath(part.source === "payload" ? intent : item, part.path);
224
+ if (value === undefined || value === null) return undefined;
225
+ if (typeof value !== "string" && typeof value !== "number") return undefined;
226
+ result += transformReadPart(String(value), part.transform);
227
+ }
228
+ return result;
229
+ }
230
+
231
+ /** Resolve a typed read-id expression for authoring-time probes and precondition planning.
232
+ * Runtime admission evaluates the custodied twin directly in Rust. */
233
+ export function resolveInvariantReadId(
234
+ id: InvariantReadId,
235
+ intent: Record<string, unknown>,
236
+ item?: unknown,
237
+ ): string | undefined {
238
+ if (id.source === "literal") return id.value;
239
+ if (id.source === "payload") {
240
+ const value = valueAtPath(intent, id.path);
241
+ if (value === undefined || value === null || value === "") return id.optional ? undefined : "";
242
+ if (typeof value !== "string" && typeof value !== "number") return id.optional ? undefined : "";
243
+ return `${id.prefix}${String(value)}${id.suffix}`;
244
+ }
245
+ return resolveTemplate(id.parts, intent, item);
246
+ }
247
+
248
+ /** Declare a bounded set of generic point reads. The returned callable keeps authoring-time tests
249
+ * ergonomic, while the non-enumerable metadata is what the compiler lowers into USDA. */
250
+ export function readSet(...bindings: readonly InvariantReadBinding[]): WorkspaceInvariantReads {
251
+ const selector = ((ctx: { intent: Record<string, unknown> }) => {
252
+ const refs: InvariantRef[] = [];
253
+ for (const binding of bindings) {
254
+ const repeated = binding.eachPayloadPath === undefined
255
+ ? [undefined]
256
+ : valueAtPath(ctx.intent, binding.eachPayloadPath);
257
+ if (!Array.isArray(repeated)) {
258
+ if (binding.eachPayloadPath !== undefined && binding.id.source === "template" && binding.id.optional) continue;
259
+ if (binding.eachPayloadPath !== undefined) continue;
260
+ }
261
+ const items: readonly unknown[] = Array.isArray(repeated) ? repeated : [undefined];
262
+ for (let index = 0; index < items.length; index += 1) {
263
+ let id = resolveInvariantReadId(binding.id, ctx.intent, items[index]);
264
+ if (id === undefined && binding.id.source !== "literal" && binding.id.optional) continue;
265
+ id ??= "";
266
+ refs.push({
267
+ name: binding.eachPayloadPath === undefined ? binding.binding : `${binding.binding}${index}`,
268
+ aggregate: binding.aggregate,
269
+ id: binding.state === "pre" ? `pre:${id}` : id,
270
+ });
271
+ }
272
+ }
273
+ return refs;
274
+ }) as NativeReadSet;
275
+ Object.defineProperty(selector, NATIVE_READ_SET, {
276
+ value: bindings.map((binding) => ({ ...binding })),
277
+ enumerable: false,
278
+ writable: false,
279
+ });
280
+ return selector;
281
+ }
282
+
283
+ /** Compiler-only access to a native read set. Ordinary callbacks deliberately return undefined. */
284
+ export function nativeReadBindings(
285
+ reads: WorkspaceInvariantReads,
286
+ ): readonly InvariantReadBinding[] | undefined {
287
+ return (reads as Partial<NativeReadSet>)[NATIVE_READ_SET];
288
+ }
289
+
54
290
  /** `inventory.Item` → `item`; `layout.FloorPlan` → `floorPlan` (short segment, first-lower). */
55
291
  function deriveName(aggregateType: string): string {
56
292
  const short = aggregateType.includes(".")
@@ -140,6 +376,8 @@ export interface WorkspaceInvariantDecl {
140
376
  readonly on: string;
141
377
  /** The executable declared read-set — ships in the engine bundle, NOT the manifest. */
142
378
  readonly reads: WorkspaceInvariantReads;
379
+ /** Typed generic read recipes lowered into USDA. Absent only for not-yet-migrated callbacks. */
380
+ readonly __nomosReadBindings?: readonly InvariantReadBinding[];
143
381
  /** The executable predicate — ships in the engine bundle, NOT the manifest. */
144
382
  readonly assert: WorkspaceInvariantAssert;
145
383
  /**
@@ -150,6 +388,11 @@ export interface WorkspaceInvariantDecl {
150
388
  * a secret-correlated refusal.
151
389
  */
152
390
  readonly declassify?: string;
391
+ /** Framework-owned evaluator whose complete semantics are part of the named kernel ABI.
392
+ * Tenant-authored invariants omit this and execute their sealed USDA plan. */
393
+ readonly __nomosEvaluator?: "birth-quota-v1" | "founder-signer-write-once-v1" | "point-guard-v1";
394
+ /** Typed point-state law consumed directly by the kernel when evaluator is `point-guard-v1`. */
395
+ readonly __nomosPointGuard?: PointGuardSpec;
153
396
  /** Framework-only provenance used to keep compiler errors on the tenant-authored surface. */
154
397
  readonly __nomosGenerated?: "directive-precondition";
155
398
  /** Brand so a plain object is never mistaken for a declaration. */
@@ -173,10 +416,12 @@ export function workspaceInvariant(id: string): {
173
416
  } {
174
417
  const finish = (directiveId: string, reads: WorkspaceInvariantReads, declassify: string | undefined) => ({
175
418
  assert(assert: WorkspaceInvariantAssert): WorkspaceInvariantDecl {
419
+ const readBindings = nativeReadBindings(reads);
176
420
  return {
177
421
  id,
178
422
  on: directiveId,
179
423
  reads,
424
+ ...(readBindings === undefined ? {} : { __nomosReadBindings: readBindings }),
180
425
  assert,
181
426
  ...(declassify !== undefined ? { declassify } : {}),
182
427
  __isWorkspaceInvariant: true as const,
@@ -74,7 +74,7 @@ import { t } from "../fields.js";
74
74
  import { set, withMarker } from "../ops.js";
75
75
  import { query } from "../query.js";
76
76
  import { read } from "../read.js";
77
- import { workspaceInvariant, refAs, type WorkspaceInvariantVerdict } from "./workspace_invariant.js";
77
+ import { readRef, readSet, workspaceInvariant, refAs, type WorkspaceInvariantVerdict } from "./workspace_invariant.js";
78
78
  import { writeTuple, removeTuple, RelationTuple, relationTupleInstanceId } from "./relations.js";
79
79
  import { relationType, self, user, key } from "../relation_schema.js";
80
80
  import { GENESIS_WARRANT_ID } from "./genesis.js";
@@ -143,7 +143,7 @@ export const workspaceSelfWriteOnce = workspaceInvariant("WorkspaceSelfWriteOnce
143
143
  // NEVER match the bare runtime id ⇒ the gate stays silently INERT (the #266 side-door bug,
144
144
  // 2026-06-15). Bare matches — exactly the convention the sharding invariants already fire under.
145
145
  .on("ensureWorkspaceSelf")
146
- .reads(() => [refAs("self", "WorkspaceSelf", "pre:" + WORKSPACE_SELF_ID)])
146
+ .reads(readSet(readRef("self", WorkspaceSelf).pre().at(WORKSPACE_SELF_ID)))
147
147
  // WRITE-ONCE on the self-record's PRESENCE via `name` (not the vestigial `role`). At genesis name + role
148
148
  // were always co-written, so an existing born workspace has `name` ⇒ behaviour preserved; a fresh one has
149
149
  // neither ⇒ first set admitted.
@@ -542,15 +542,14 @@ export function buildGenesisChain(p: {
542
542
  rebornFrom?: string;
543
543
  }): string {
544
544
  const founder = "user:" + p.principal;
545
- // THE ORDER-DECLARED LAW INSTALL (G3): the child installs the EXACT law the order names — bytes are NOT in
546
- // the recipe (the kernel resolves them by hash from its own custody when it folds the birth, never the
547
- // host). `domainHash` names the law; `domainPackageB64` stays empty here (the kernel supplies bytes-by-hash
548
- // for the in-chain install). Two layers, matching the genesis bootstrap: bootstrap controller, then law.
545
+ // THE ORDER-DECLARED LAW INSTALL (G3): the child installs the EXACT typed USDA closures the order names.
546
+ // Bytes are NOT in the recipe; the kernel resolves each identity from its own custody when it folds the
547
+ // birth. Two layers, matching the genesis bootstrap: bootstrap controller, then law.
549
548
  const installSteps =
550
549
  p.frameworkHash && p.lawHash
551
550
  ? [
552
- { domain: "bootstrap", directiveId: "installDomain", actor: founder, payload: { domainHash: p.frameworkHash }, domainHash: p.frameworkHash, domainPackageB64: "" },
553
- { domain: "nomos", directiveId: "installDomain", actor: founder, payload: { domainHash: p.lawHash }, domainHash: p.lawHash, domainPackageB64: "" },
551
+ { domain: "bootstrap", directiveId: "installDomain", actor: founder, payload: { domainHash: p.frameworkHash }, domainHash: p.frameworkHash },
552
+ { domain: "nomos", directiveId: "installDomain", actor: founder, payload: { domainHash: p.lawHash }, domainHash: p.lawHash },
554
553
  ]
555
554
  : [];
556
555
  // THE LINEAGE-AUTHORIZATION BINDING (proof-friendly): the child's birthCloud records who authorized it, so
@@ -800,7 +799,8 @@ export const birthCloud = directive("birthCloud")
800
799
  // the born marker; `role` is no longer written (vestigial node-class — see WorkspaceSelf).
801
800
  ensure(WorkspaceSelf, WORKSPACE_SELF_ID).set("name", p.name);
802
801
  return [];
803
- });
802
+ })
803
+ .alsoEnsures(WorkspaceSelf);
804
804
 
805
805
  /**
806
806
  * provisionWorkspace — the worker's RETURN RECEIPT: authored AFTER it physically
@@ -917,9 +917,7 @@ export const retireOwnWorkspace = directive("retireOwnWorkspace")
917
917
  * so the read manifest is unchanged (era-/evolve-safe). */
918
918
  export const retireOwnOnly = workspaceInvariant("RetireOwnOnly")
919
919
  .on("retireOwnWorkspace")
920
- .reads((ctx) => [
921
- refAs("target", "CloudWorkspace", "pre:" + String((ctx?.intent ?? {}).workspaceId ?? "")),
922
- ])
920
+ .reads(readSet(readRef("target", CloudWorkspace).pre().fromPayload("workspaceId")))
923
921
  .assert((refs, ctx) => {
924
922
  const target = (refs as { target?: { principal?: string; createdBy?: string } }).target;
925
923
  // The captured, VERIFIED author rides as a SUBJECT (`user:<uid>`); the record's creator fields
@@ -1031,9 +1029,7 @@ export const rebirthWorkspace = directive("rebirthWorkspace")
1031
1029
  */
1032
1030
  export const rebirthNotRoot = workspaceInvariant("RebirthNotRoot")
1033
1031
  .on("rebirthWorkspace")
1034
- .reads((ctx) => [
1035
- refAs("target", "CloudWorkspace", "pre:" + String((ctx?.intent ?? {}).workspaceId ?? "")),
1036
- ])
1032
+ .reads(readSet(readRef("target", CloudWorkspace).pre().fromPayload("workspaceId")))
1037
1033
  .assert((refs) => {
1038
1034
  const target = (refs as { target?: { name?: string } }).target;
1039
1035
  return target?.name === "root"
@@ -1051,9 +1047,7 @@ export const rebirthNotRoot = workspaceInvariant("RebirthNotRoot")
1051
1047
  */
1052
1048
  export const rebirthRetainsLineage = workspaceInvariant("RebirthRetainsLineage")
1053
1049
  .on("rebirthWorkspace")
1054
- .reads((ctx) => [
1055
- refAs("target", "CloudWorkspace", "pre:" + String((ctx?.intent ?? {}).workspaceId ?? "")),
1056
- ])
1050
+ .reads(readSet(readRef("target", CloudWorkspace).pre().fromPayload("workspaceId")))
1057
1051
  .assert((refs, ctx) => {
1058
1052
  const target = (refs as { target?: { parent?: string } }).target;
1059
1053
  const claimed = (ctx?.intent as { parent?: string } | undefined)?.parent;
@@ -1788,7 +1782,7 @@ export { RelationTuple };
1788
1782
  * owner-rebind the host session-lane used to refuse — is rejected. */
1789
1783
  export const genesisWarrantWriteOnce = workspaceInvariant("GenesisWarrantWriteOnce")
1790
1784
  .on("seedGenesis")
1791
- .reads(() => [refAs("warrant", "GenesisWarrant", "pre:" + GENESIS_WARRANT_ID)])
1785
+ .reads(readSet(readRef("warrant", "GenesisWarrant").pre().at(GENESIS_WARRANT_ID)))
1792
1786
  .assert(({ warrant }, ctx) => {
1793
1787
  const cur = warrant as { principal?: string; workspaceId?: string } | undefined;
1794
1788
  if (!cur?.principal) return { accept: true }; // virgin — genesis may seed the founding warrant
@@ -1823,7 +1817,7 @@ export const founderSignerWriteOnce = workspaceInvariant("FounderSignerWriteOnce
1823
1817
  * WorkspaceSelf.role exists. Genesis births on a role-less chain; materialize births each child once. */
1824
1818
  export const birthCloudOnce = workspaceInvariant("BirthCloudOnce")
1825
1819
  .on("birthCloud")
1826
- .reads(() => [refAs("self", "WorkspaceSelf", "pre:" + WORKSPACE_SELF_ID)])
1820
+ .reads(readSet(readRef("self", WorkspaceSelf).pre().at(WORKSPACE_SELF_ID)))
1827
1821
  // WRITE-ONCE on the self-record's PRESENCE via `name` (not the vestigial `role`) — a workspace is born once.
1828
1822
  .assert(({ self }) =>
1829
1823
  (self as { name?: string })?.name ? { reject: "cloud-already-born" } : { accept: true },
@@ -1837,7 +1831,7 @@ export const birthCloudOnce = workspaceInvariant("BirthCloudOnce")
1837
1831
  * rest. (Worker authors ownerGrantHash=foundingAdminHash for platforms, sha256(freshSecret) for plain.) */
1838
1832
  export const provisionReceiptGuard = workspaceInvariant("ProvisionReceiptGuard")
1839
1833
  .on("provisionWorkspace")
1840
- .reads((ctx) => [refAs("ws", "CloudWorkspace", "pre:" + String((ctx.intent ?? {}).workspaceId ?? ""))])
1834
+ .reads(readSet(readRef("ws", CloudWorkspace).pre().fromPayload("workspaceId")))
1841
1835
  .assert(({ ws }, ctx) => {
1842
1836
  const rec = ws as { status?: string; foundingAdminHash?: string } | undefined;
1843
1837
  if (!rec || rec.status !== "requested") return { reject: "provision-requires-requested-workspace" };
@@ -1856,7 +1850,7 @@ export const provisionReceiptGuard = workspaceInvariant("ProvisionReceiptGuard")
1856
1850
  * refuses reclaiming itself; this guards children.) */
1857
1851
  export const reclaimRequiresRetired = workspaceInvariant("ReclaimRequiresRetired")
1858
1852
  .on("reclaimWorkspace")
1859
- .reads((ctx) => [refAs("ws", "CloudWorkspace", "pre:" + String((ctx.intent ?? {}).workspaceId ?? ""))])
1853
+ .reads(readSet(readRef("ws", CloudWorkspace).pre().fromPayload("workspaceId")))
1860
1854
  .assert(({ ws }) => {
1861
1855
  const rec = ws as { status?: string } | undefined;
1862
1856
  return rec?.status === "retired" ? { accept: true } : { reject: "reclaim-requires-retired" };
@@ -1915,11 +1909,11 @@ function creationPostureVerdict(
1915
1909
  return { reject: "creation.invalid-posture" };
1916
1910
  }
1917
1911
 
1918
- const creationPostureReads = () => [
1919
- refAs("policy", "CloudPolicy", CREATION_POLICY_ID),
1920
- refAs("creators", "RelationTuple", CREATOR_TUPLE_ID),
1921
- refAs("admins", "RelationTuple", ADMIN_TUPLE_ID),
1922
- ];
1912
+ const creationPostureReads = readSet(
1913
+ readRef("policy", CloudPolicy).at(CREATION_POLICY_ID),
1914
+ readRef("creators", RelationTuple).at(CREATOR_TUPLE_ID),
1915
+ readRef("admins", RelationTuple).at(ADMIN_TUPLE_ID),
1916
+ );
1923
1917
 
1924
1918
  /** Compatibility create: open/unset ⇒ proven author; invite ⇒ creator; closed ⇒ admin. */
1925
1919
  export const creationPostureCreate = workspaceInvariant("CreationPostureCreate")
@@ -1961,7 +1955,7 @@ export const workspaceQuotaCreate = workspaceInvariant("WorkspaceQuota")
1961
1955
  .on("createWorkspace")
1962
1956
  // The snapshot ref-set is EMPTY — the count comes from the captured reads (ctx.reads), which the standard
1963
1957
  // ref-resolution cannot produce (it resolves single instances by id, never an enumeration).
1964
- .reads(() => [])
1958
+ .reads(readSet())
1965
1959
  .assert((_snapshots, ctx) =>
1966
1960
  quotaVerdict(ctx?.reads ?? [], String((ctx?.intent ?? {}).principal ?? "")),
1967
1961
  );
@@ -1969,7 +1963,7 @@ export const workspaceQuotaCreate = workspaceInvariant("WorkspaceQuota")
1969
1963
  /** birthChild: the SAME count quota (a leaf or platform birth draws on the principal's allowance). */
1970
1964
  export const workspaceQuotaBirth = workspaceInvariant("WorkspaceQuotaBirth")
1971
1965
  .on("birthChild")
1972
- .reads(() => [])
1966
+ .reads(readSet())
1973
1967
  .assert((_snapshots, ctx) =>
1974
1968
  quotaVerdict(ctx?.reads ?? [], String((ctx?.intent ?? {}).principal ?? "")),
1975
1969
  );