@githolon/dsl 0.102.5 → 0.103.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/compile_package.mjs +43 -2
- package/compile_proof.d.mts +47 -0
- package/compile_proof.mjs +343 -0
- package/dart/CHANGELOG.md +14 -0
- package/dart/lib/src/dev_cloud.dart +8 -8
- package/dart/lib/src/nms_generated_runtime.dart +2 -0
- package/dart/lib/src/runner/node-entry.mjs +5539 -1228
- package/dart/lib/src/runner/offline-install.mjs +4578 -528
- package/dart/lib/src/test_harness.dart +206 -152
- package/dart/lib/src/webview_bridge.dart +239 -102
- package/dart/pubspec.lock +1 -1
- package/dart/pubspec.yaml +2 -2
- package/dart/test/acquire_session_credentials_test.dart +4 -3
- package/dart/test/acquire_session_test.dart +9 -0
- package/dart/test/adv_realm_interference_test.dart +7 -6
- package/dart/test/cloud_bound_topology_test.dart +3 -3
- package/dart/test/cluster_births_test.dart +4 -3
- package/dart/test/cluster_device_keyed_acceptance_test.dart +10 -16
- package/dart/test/cluster_local_autosync_test.dart +3 -2
- package/dart/test/cluster_scale_test.dart +3 -2
- package/dart/test/dlq_session_surface_test.dart +7 -6
- package/dart/test/first_app_gauntlet_test.dart +14 -21
- package/dart/test/fixtures/wedge/fake-install.mjs +1 -1
- package/dart/test/four_layer_nested_topology_test.dart +4 -4
- package/dart/test/harness_phase_timeout_test.dart +2 -2
- package/dart/test/local_sync_inert_test.dart +1 -1
- package/dart/test/parent_signer_recovery_test.dart +2 -2
- package/dart/test/sealing_client_test.dart +4 -3
- package/dart/test/session_bound_test.dart +8 -7
- package/dart/test/test_harness_test.dart +13 -12
- package/dart/test/vo_read_typing_test.dart +4 -3
- package/dart/test/watch_after_offer_stall_test.dart +4 -3
- package/dart/test/webview_bridge_test.dart +102 -5
- package/dart_types/CHANGELOG.md +5 -0
- package/dart_types/lib/nomos_types.dart +9 -0
- package/dart_types/lib/src/nomos_ref.dart +20 -0
- package/dart_types/lib/src/optional_ref.dart +85 -0
- package/dart_types/lib/src/sha256_digest.dart +41 -0
- package/dart_types/lib/src/versioned_nomos_ref.dart +81 -0
- package/dart_types/pubspec.yaml +1 -1
- package/dart_types/test/optional_ref_test.dart +44 -0
- package/dart_types/test/sha256_digest_test.dart +18 -0
- package/package.json +9 -5
- package/runtime/generated-application-host.js +5365 -1105
- package/src/aggregate.ts +18 -0
- package/src/application_composition.ts +6 -32
- package/src/attested_core.ts +6 -3
- package/src/birth_meter.ts +1 -0
- package/src/birth_meter_ensure.ts +28 -2
- package/src/births.ts +1 -1
- package/src/build_package.ts +162 -538
- package/src/business_birth.ts +19 -3
- package/src/canonical_json.ts +21 -0
- package/src/check_package_main.ts +73 -33
- package/src/clear_directive.ts +19 -8
- package/src/codegen_dart.ts +305 -22
- package/src/codegen_dart_application.ts +862 -292
- package/src/codegen_dart_client.ts +1084 -407
- package/src/codegen_dart_scenario.ts +12 -12
- package/src/codegen_proof.ts +169 -39
- package/src/codegen_provider_dart.ts +17 -1
- package/src/codegen_ts.ts +298 -106
- package/src/compat_manifest.ts +2 -2
- package/src/compat_package_main.ts +8 -12
- package/src/compile_package_main.ts +1860 -592
- package/src/compose.ts +1 -1
- package/src/directive.ts +208 -15
- package/src/engine_entry.ts +165 -111
- package/src/evolve_preview.ts +29 -69
- package/src/extremum.ts +1 -1
- package/src/fields.ts +183 -4
- package/src/flow_gate.ts +75 -2
- package/src/framework/application_home.ts +2 -0
- package/src/framework/auth_provider_law.ts +2 -2
- package/src/framework/birth_meter.ts +7 -19
- package/src/framework/bootstrap.ts +0 -2
- package/src/framework/business_corrections.ts +9 -5
- package/src/framework/domain_lifecycle.ts +15 -29
- package/src/framework/encryption_scope.ts +2 -1
- package/src/framework/framework_aggregates.ts +2 -0
- package/src/framework/genesis.ts +16 -14
- package/src/framework/home.ts +20 -14
- package/src/framework/owned_custody.ts +9 -3
- package/src/framework/platform_admin.ts +3 -3
- package/src/framework/relations.ts +21 -13
- package/src/framework/signing_identity.ts +45 -40
- package/src/framework/workspace_invariant.ts +245 -0
- package/src/framework/workspaces.ts +23 -29
- package/src/index.ts +36 -16
- package/src/intent.ts +178 -6
- package/src/law_check.ts +32 -11
- package/src/law_graph.ts +2902 -0
- package/src/law_graph_usda.ts +1799 -0
- package/src/law_identity.ts +435 -0
- package/src/law_value_type.ts +230 -0
- package/src/lsp_server.ts +58 -29
- package/src/ops.ts +7 -0
- package/src/package_config.ts +141 -0
- package/src/package_dependencies.ts +108 -0
- package/src/package_dependency_usage.ts +150 -0
- package/src/package_source_imports.ts +71 -0
- package/src/plan_source_slice.ts +335 -0
- package/src/point_guard.ts +192 -0
- package/src/precondition.ts +44 -9
- package/src/read_projection_namespace.ts +1 -1
- package/src/runtime_zod.ts +44 -0
- package/src/secondary_ensure.ts +67 -14
- package/src/shape_compat.ts +1 -1
- package/src/stable_id_identity_bridge.ts +149 -0
- package/src/stable_id_lockfile.ts +140 -6
- package/src/stable_ids.ts +1 -1
- package/src/stable_ids_for_module.ts +348 -0
- package/src/stable_ids_types.ts +1 -1
- package/src/sum.ts +1 -1
- package/src/usd_state.ts +1 -1
- package/src/usda_relationship_integrity.ts +66 -0
- package/src/usda_text.ts +18 -0
- package/src/usdz.ts +78 -0
- package/src/value_object.ts +95 -0
- package/src/wire.ts +19 -0
- package/src/wire_encode.ts +22 -0
- package/src/workspace_sharding.ts +52 -36
- package/src/workspace_sharing.ts +28 -4
- package/src/zod_introspection.ts +1 -1
- package/dart/test/cluster_attested_test.dart +0 -201
- package/src/compile_engine.ts +0 -595
- package/src/manifest.ts +0 -1476
- package/src/read_projection_composition.ts +0 -117
- package/src/read_projection_manifest.ts +0 -115
- package/src/relation_composition.ts +0 -286
- package/src/usd.ts +0 -1967
- package/src/usd_layers.ts +0 -1776
package/src/engine_entry.ts
CHANGED
|
@@ -63,13 +63,7 @@ import { synthesizeBirthQuotaInvariants } from "./birth_meter.js";
|
|
|
63
63
|
import { withBirthMeterExports } from "./birth_meter_ensure.js";
|
|
64
64
|
import { expandRoleExports } from "./role.js";
|
|
65
65
|
import { withAttestedReadExports } from "./attested.js";
|
|
66
|
-
import {
|
|
67
|
-
import type { QueryDecl } from "./query.js";
|
|
68
|
-
import type { AnyCount } from "./count.js";
|
|
69
|
-
import type { AnySum } from "./sum.js";
|
|
70
|
-
import type { AnyExtremum } from "./extremum.js";
|
|
71
|
-
import type { SpatialDecl } from "./spatial.js";
|
|
72
|
-
import type { DomainModule } from "./codegen_dart.js";
|
|
66
|
+
import type { OpenApiDoc } from "./codegen_openapi.js";
|
|
73
67
|
import type { Directive } from "./directive.js";
|
|
74
68
|
import type { AggregateHandle, AggregateInvariantFn, AggregateInvariantVerdict } from "./aggregate.js";
|
|
75
69
|
import type { Ports } from "./ctx.js";
|
|
@@ -84,6 +78,7 @@ import { expandBusinessBirthExports, isBusinessBirthDirective } from "./business
|
|
|
84
78
|
import { withOwnedCustodyExports } from "./framework/owned_custody.js";
|
|
85
79
|
import { withBusinessCorrectionExports } from "./framework/business_corrections.js";
|
|
86
80
|
import { withClearDirectives } from "./clear_directive.js";
|
|
81
|
+
import { withWorkspaceSharingInvitationExports } from "./workspace_sharing.js";
|
|
87
82
|
import {
|
|
88
83
|
applicationLanguageOf,
|
|
89
84
|
assertApplicationLanguageCoversAuthoredDirectives,
|
|
@@ -123,6 +118,19 @@ export interface EngineRouting {
|
|
|
123
118
|
readonly relationOf?: Record<string, readonly string[]>;
|
|
124
119
|
/** workspace-invariant id → declaring domain keys (#266 — the gate's reads/assert dispatches). */
|
|
125
120
|
readonly workspaceInvariantOf?: Record<string, readonly string[]>;
|
|
121
|
+
/**
|
|
122
|
+
* Dispatch-domain label → canonical USDA domain identity (`d_<sid>`). Tuple object types use this
|
|
123
|
+
* durable namespace so a domain rename cannot rewrite Zanzibar identity.
|
|
124
|
+
*/
|
|
125
|
+
readonly relationNamespaceOf?: Record<string, string>;
|
|
126
|
+
/**
|
|
127
|
+
* Domain key → every non-global aggregate type in the complete compiled domain. A selectively
|
|
128
|
+
* sliced plan may retain a secondary aggregate only inside a closure, so reconstructing this set
|
|
129
|
+
* from the slice's exported declarations can silently lose the type's namespace.
|
|
130
|
+
*/
|
|
131
|
+
readonly namespacedAggregatesOf?: Record<string, readonly string[]>;
|
|
132
|
+
/** Domain key → every local relation shape in the complete compiled domain, for the same reason. */
|
|
133
|
+
readonly qualifiableRelationsOf?: Record<string, readonly string[]>;
|
|
126
134
|
}
|
|
127
135
|
|
|
128
136
|
/** The one declarative input: dispatch key → ORDERED module list (later wins). */
|
|
@@ -133,6 +141,19 @@ export interface EngineEntryConfig {
|
|
|
133
141
|
* Values may be thunks (lazy boot — see the module header).
|
|
134
142
|
*/
|
|
135
143
|
readonly domains: Record<string, readonly DomainModuleSource[]>;
|
|
144
|
+
/**
|
|
145
|
+
* Compiler-authenticated exact routes may share one domain-agnostic framework plan. When the
|
|
146
|
+
* requested dispatch domain is absent from `domains`, prepare and execute this sole registry while
|
|
147
|
+
* retaining the requested domain for relation namespacing. Never authored by tenant code.
|
|
148
|
+
*/
|
|
149
|
+
readonly routedDomainFallback?: string;
|
|
150
|
+
/**
|
|
151
|
+
* Domain-local dependency initializers emitted by the compiler. They run one per
|
|
152
|
+
* `prepareDomain()` call before the domain's composable sources and their exports are discarded.
|
|
153
|
+
* This turns a tenant module's cross-domain ES import closure into bounded maintenance quanta
|
|
154
|
+
* without registering the imported domain's declarations in the caller's slice.
|
|
155
|
+
*/
|
|
156
|
+
readonly preloads?: Record<string, readonly (() => unknown)[]>;
|
|
136
157
|
/** Optional report registry: `reportId` → a factory `(actor) => Report`. */
|
|
137
158
|
readonly reports?: Record<string, (actor: string) => EngineReport>;
|
|
138
159
|
/** REQUIRED when any domain module is a thunk; ignored otherwise. */
|
|
@@ -147,16 +168,11 @@ export interface EngineEntryConfig {
|
|
|
147
168
|
*/
|
|
148
169
|
readonly seeds?: readonly unknown[];
|
|
149
170
|
/**
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
* already auto-discovered + named-resolved them when it composed each DomainModule). The
|
|
154
|
-
* directives + aggregates ARE discovered from the modules; these ride alongside so the
|
|
155
|
-
* lump's `globalThis.describe` reproduces the EXACT `<name>.openapi.json` the compiler wrote
|
|
156
|
-
* (byte-identity, no Rust re-implementation, no drift). Keyed by domain dispatch key, in the
|
|
157
|
-
* same declaration order as `domains`. Omitted ⇒ describe still emits offers from directives.
|
|
171
|
+
* Compiler-derived self-description. It is a presentation projection held inside the separately
|
|
172
|
+
* routed `describe` plan, never executable law IR. Ordinary offer/read plans omit it completely,
|
|
173
|
+
* so OpenAPI/JSON-Schema generation cannot sit on their parse or readiness flame path.
|
|
158
174
|
*/
|
|
159
|
-
readonly
|
|
175
|
+
readonly openapiDocument?: OpenApiDoc;
|
|
160
176
|
/**
|
|
161
177
|
* The package's OpenAPI document title (the compiler's `"<name> (nomos package)"`). Baked into
|
|
162
178
|
* the lump so `describe` reproduces the compiler's `<name>.openapi.json` byte-for-byte without
|
|
@@ -165,17 +181,6 @@ export interface EngineEntryConfig {
|
|
|
165
181
|
readonly packageTitle?: string;
|
|
166
182
|
}
|
|
167
183
|
|
|
168
|
-
/** The openapi-relevant decls one domain contributes beyond its directives/aggregates. */
|
|
169
|
-
export interface OpenApiDomainDecls {
|
|
170
|
-
readonly queries?: readonly QueryDecl[];
|
|
171
|
-
readonly counts?: readonly AnyCount[];
|
|
172
|
-
readonly sums?: readonly AnySum[];
|
|
173
|
-
readonly mins?: readonly AnyExtremum[];
|
|
174
|
-
readonly maxes?: readonly AnyExtremum[];
|
|
175
|
-
readonly spatials?: readonly SpatialDecl[];
|
|
176
|
-
readonly workspaceInvariants?: readonly WorkspaceInvariantDecl[];
|
|
177
|
-
}
|
|
178
|
-
|
|
179
184
|
interface RegistryEntry {
|
|
180
185
|
directive: Directive<unknown>;
|
|
181
186
|
agg: AggregateHandle;
|
|
@@ -264,6 +269,7 @@ function mergeModules(mods: readonly DomainModuleExports[], domain: string): Dom
|
|
|
264
269
|
[...directivesOf(merged).values()],
|
|
265
270
|
domain,
|
|
266
271
|
);
|
|
272
|
+
merged = withWorkspaceSharingInvitationExports(merged, applicationLanguage.workspaceSharing);
|
|
267
273
|
}
|
|
268
274
|
merged = expandBusinessBirthExports(merged, domain);
|
|
269
275
|
merged = withOwnedCustodyExports(merged);
|
|
@@ -327,6 +333,20 @@ function buildDomainSlice(mod: DomainModuleExports, domain: string): DomainSlice
|
|
|
327
333
|
? discoveredDirectives
|
|
328
334
|
: executableDirectivesForLanguage(applicationLanguage, discoveredDirectives, domain);
|
|
329
335
|
const dirs = new Map(executableDirectives.map((directive) => [directive.id, directive]));
|
|
336
|
+
// A directive already owns the typed aggregate handle it targets. Retain that
|
|
337
|
+
// link unconditionally in the EXECUTABLE slice instead of requiring a second
|
|
338
|
+
// aggregate re-export. For a cross-workspace birth this does not copy child
|
|
339
|
+
// schema into the parent's canonical law: it only gives this selected runtime
|
|
340
|
+
// plan enough structure to register and validate its declared offer. Canonical
|
|
341
|
+
// package composition keeps its separate child-law dependency rule.
|
|
342
|
+
for (const directive of discoveredDirectives) {
|
|
343
|
+
if (directive.aggregate !== undefined) {
|
|
344
|
+
aggs.set(directive.aggregate.id, directive.aggregate);
|
|
345
|
+
}
|
|
346
|
+
for (const output of directive.secondaryOutputs ?? []) {
|
|
347
|
+
if (output.aggregate !== undefined) aggs.set(output.aggregate.id, output.aggregate);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
330
350
|
// RELATIONS AUTO-ENSURE (mirror of composeDomainModule): a relate-family directive `.ensures(RelationTuple)`
|
|
331
351
|
// needs the framework aggregate registered for DISPATCH too — without it the directive is silently dropped
|
|
332
352
|
// below. Inject iff a directive targets it and the dev didn't already export the handle.
|
|
@@ -496,12 +516,16 @@ export function collectEngineRouting(
|
|
|
496
516
|
const aggregateInvariantOf: Record<string, string[]> = {};
|
|
497
517
|
const relationOf: Record<string, string[]> = {};
|
|
498
518
|
const workspaceInvariantOf: Record<string, string[]> = {};
|
|
519
|
+
const namespacedAggregatesOf: Record<string, string[]> = {};
|
|
520
|
+
const qualifiableRelationsOf: Record<string, string[]> = {};
|
|
499
521
|
const add = (map: Record<string, string[]>, key: string, domainName: string) => {
|
|
500
522
|
const list = map[key] ?? (map[key] = []);
|
|
501
523
|
if (!list.includes(domainName)) list.push(domainName);
|
|
502
524
|
};
|
|
503
525
|
for (const [domainName, mods] of Object.entries(domains)) {
|
|
504
526
|
const slice = buildDomainSlice(mergeModules(mods, domainName), domainName);
|
|
527
|
+
namespacedAggregatesOf[domainName] = [...slice.qualifiableAggregates].sort();
|
|
528
|
+
qualifiableRelationsOf[domainName] = [...slice.qualifiableRelations].sort();
|
|
505
529
|
// Type-keyed dispatches arrive with the ROW's type — QUALIFIED for a namespaced aggregate (the
|
|
506
530
|
// runtime `__type` stamp), bare for a global one / a pre-namespacing row. Route BOTH keys so
|
|
507
531
|
// either era resolves to the declaring domain (the slice lookup strips the qualifier back off).
|
|
@@ -513,7 +537,15 @@ export function collectEngineRouting(
|
|
|
513
537
|
for (const relationId of slice.invariants.keys()) add(relationOf, relationId, domainName);
|
|
514
538
|
for (const wsInvariantId of slice.wsInvariants.keys()) add(workspaceInvariantOf, wsInvariantId, domainName);
|
|
515
539
|
}
|
|
516
|
-
return {
|
|
540
|
+
return {
|
|
541
|
+
derivedOf,
|
|
542
|
+
combinedOf,
|
|
543
|
+
aggregateInvariantOf,
|
|
544
|
+
relationOf,
|
|
545
|
+
workspaceInvariantOf,
|
|
546
|
+
namespacedAggregatesOf,
|
|
547
|
+
qualifiableRelationsOf,
|
|
548
|
+
};
|
|
517
549
|
}
|
|
518
550
|
|
|
519
551
|
/**
|
|
@@ -551,6 +583,10 @@ function portsFromHost(principalRaw = ""): Ports {
|
|
|
551
583
|
export interface RegisteredEngine {
|
|
552
584
|
plan(job: unknown): WireEvent[] | { events: WireEvent[]; strikes?: string[]; endorse?: { sink: string; facet: string; reason: string | null }[] };
|
|
553
585
|
planReport(job: unknown): string;
|
|
586
|
+
/** Initialize at most one lazy dependency/module, or compose an initialized domain slice. */
|
|
587
|
+
prepareDomain(domain: string): { complete: boolean; initialized: number; total: number };
|
|
588
|
+
/** Compiler/tooling probe: force one domain and prove an exact routed offer is registered. */
|
|
589
|
+
hasDirective(domain: string, directiveId: string): boolean;
|
|
554
590
|
/** The G1 self-describe verb: the lump's own merged OpenAPI, derived from its modules. */
|
|
555
591
|
describe(opts?: { lawHash?: string; title?: string }): OpenApiDoc;
|
|
556
592
|
}
|
|
@@ -565,6 +601,8 @@ export interface RegisteredEngine {
|
|
|
565
601
|
*/
|
|
566
602
|
export function registerEngine(config: EngineEntryConfig): RegisteredEngine {
|
|
567
603
|
const domainNames = Object.keys(config.domains);
|
|
604
|
+
const sourceDomainFor = (requested: string): string =>
|
|
605
|
+
config.domains[requested] !== undefined ? requested : (config.routedDomainFallback ?? requested);
|
|
568
606
|
const lazy = Object.values(config.domains).some((mods) =>
|
|
569
607
|
mods.some((m) => typeof m === "function"),
|
|
570
608
|
);
|
|
@@ -581,15 +619,61 @@ export function registerEngine(config: EngineEntryConfig): RegisteredEngine {
|
|
|
581
619
|
|
|
582
620
|
// ── domain key → its slice (forced once per sandbox; eager boot forces ALL now) ──
|
|
583
621
|
const sliceCache = new Map<string, DomainSlice>();
|
|
622
|
+
const initializedPreloads = new Map<string, number>();
|
|
623
|
+
const initializedSources = new Map<string, DomainModuleExports[]>();
|
|
624
|
+
function prepareDomain(domainName: string): { complete: boolean; initialized: number; total: number } {
|
|
625
|
+
domainName = sourceDomainFor(domainName);
|
|
626
|
+
const sources = config.domains[domainName];
|
|
627
|
+
if (sources === undefined) return { complete: true, initialized: 0, total: 0 };
|
|
628
|
+
const preloads = config.preloads?.[domainName] ?? [];
|
|
629
|
+
const total = preloads.length + sources.length;
|
|
630
|
+
if (sliceCache.has(domainName)) {
|
|
631
|
+
return { complete: true, initialized: total, total };
|
|
632
|
+
}
|
|
633
|
+
const preloaded = initializedPreloads.get(domainName) ?? 0;
|
|
634
|
+
if (preloaded < preloads.length) {
|
|
635
|
+
preloads[preloaded]!();
|
|
636
|
+
initializedPreloads.set(domainName, preloaded + 1);
|
|
637
|
+
return { complete: false, initialized: preloaded + 1, total };
|
|
638
|
+
}
|
|
639
|
+
let initialized = initializedSources.get(domainName);
|
|
640
|
+
if (initialized === undefined) {
|
|
641
|
+
initialized = [];
|
|
642
|
+
initializedSources.set(domainName, initialized);
|
|
643
|
+
}
|
|
644
|
+
if (initialized.length < sources.length) {
|
|
645
|
+
const source = sources[initialized.length]!;
|
|
646
|
+
initialized.push(typeof source === "function" ? source() : source);
|
|
647
|
+
return { complete: false, initialized: preloads.length + initialized.length, total };
|
|
648
|
+
}
|
|
649
|
+
const slice = buildDomainSlice(mergeModules(initialized, domainName), domainName);
|
|
650
|
+
// The compiler computes these classifications from the complete domain before plan slicing.
|
|
651
|
+
// Prefer that authenticated inventory over the reduced export bag: a closure-retained secondary
|
|
652
|
+
// aggregate/relation is executable law even when tree-shaking removes its top-level export.
|
|
653
|
+
const compiledAggregates = routing.namespacedAggregatesOf?.[domainName];
|
|
654
|
+
if (compiledAggregates !== undefined) {
|
|
655
|
+
slice.qualifiableAggregates = new Set(compiledAggregates);
|
|
656
|
+
}
|
|
657
|
+
const compiledRelations = routing.qualifiableRelationsOf?.[domainName];
|
|
658
|
+
if (compiledRelations !== undefined) {
|
|
659
|
+
slice.qualifiableRelations = new Set(compiledRelations);
|
|
660
|
+
}
|
|
661
|
+
sliceCache.set(domainName, slice);
|
|
662
|
+
initializedSources.delete(domainName);
|
|
663
|
+
initializedPreloads.delete(domainName);
|
|
664
|
+
return { complete: true, initialized: total, total };
|
|
665
|
+
}
|
|
584
666
|
function sliceOf(domainName: string): DomainSlice | undefined {
|
|
667
|
+
domainName = sourceDomainFor(domainName);
|
|
585
668
|
const cached = sliceCache.get(domainName);
|
|
586
669
|
if (cached !== undefined) return cached;
|
|
587
670
|
const sources = config.domains[domainName];
|
|
588
671
|
if (sources === undefined) return undefined;
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
672
|
+
while (!prepareDomain(domainName).complete) { /* synchronous compatibility lane */ }
|
|
673
|
+
return sliceCache.get(domainName);
|
|
674
|
+
}
|
|
675
|
+
function hasDirective(domainName: string, directiveId: string): boolean {
|
|
676
|
+
return sliceOf(domainName)?.registry.has(directiveId) === true;
|
|
593
677
|
}
|
|
594
678
|
if (!lazy) for (const domainName of domainNames) sliceOf(domainName);
|
|
595
679
|
|
|
@@ -698,23 +782,9 @@ export function registerEngine(config: EngineEntryConfig): RegisteredEngine {
|
|
|
698
782
|
return found;
|
|
699
783
|
}
|
|
700
784
|
|
|
701
|
-
//
|
|
702
|
-
//
|
|
703
|
-
|
|
704
|
-
/** `{workspaceInvariantList:{}}` → the DECLARED set `[{id,on},…]` sorted by id (discovery). */
|
|
705
|
-
function planWorkspaceInvariantList(): string {
|
|
706
|
-
const declared = new Map<string, { id: string; on: string }>();
|
|
707
|
-
const domains =
|
|
708
|
-
routing.workspaceInvariantOf !== undefined
|
|
709
|
-
? [...new Set(Object.values(routing.workspaceInvariantOf).flat())]
|
|
710
|
-
: domainNames;
|
|
711
|
-
for (const domainName of domains) {
|
|
712
|
-
const slice = sliceOf(domainName);
|
|
713
|
-
if (slice === undefined) continue;
|
|
714
|
-
for (const w of slice.wsInvariants.values()) declared.set(w.id, { id: w.id, on: w.on });
|
|
715
|
-
}
|
|
716
|
-
return JSON.stringify([...declared.values()].sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)));
|
|
717
|
-
}
|
|
785
|
+
// Workspace-invariant read selection uses typed USDA interpreted by Rust whenever the
|
|
786
|
+
// compiler can describe it. This dispatch remains the deterministic JavaScript fallback
|
|
787
|
+
// for tenant selectors that deliberately exceed that bounded read algebra.
|
|
718
788
|
|
|
719
789
|
/** `{workspaceInvariantReads:{id}, payload}` → the bounded ref-set `[{name,aggregate,id},…]`. */
|
|
720
790
|
function planWorkspaceInvariantReads(job: {
|
|
@@ -732,11 +802,7 @@ export function registerEngine(config: EngineEntryConfig): RegisteredEngine {
|
|
|
732
802
|
throw new Error(`engine workspaceInvariantReads: no workspace invariant registered for "${id}"`);
|
|
733
803
|
}
|
|
734
804
|
const intent = (job.intent?.payload ?? {}) as Record<string, unknown>;
|
|
735
|
-
// LAW-GATED AUTHZ: the captured author identity (a top-level sibling of `intent`, set by the wasm
|
|
736
|
-
// author leg + replayed verbatim) — the governance invariant reads it to gate by the AUTHOR.
|
|
737
805
|
const principal = (job.capturedPorts?.principal ?? "") as string;
|
|
738
|
-
// VA-core (Phase 3): the kernel-DERIVED author-verified flag (`captured_ports.principal_verified`),
|
|
739
|
-
// threaded the same way as `principal`, so an invariant may gate on a VERIFIED author (e.g. enrollSignerSelf).
|
|
740
806
|
const principalVerified = job.capturedPorts?.principal_verified === true;
|
|
741
807
|
return JSON.stringify(decl.reads({ intent, principal, principalVerified }));
|
|
742
808
|
}
|
|
@@ -848,20 +914,16 @@ export function registerEngine(config: EngineEntryConfig): RegisteredEngine {
|
|
|
848
914
|
queryRows?: unknown[];
|
|
849
915
|
priorState?: Record<string, unknown>;
|
|
850
916
|
}): string {
|
|
851
|
-
// Branch order mirrors emit_engine.ts: workspace-invariant
|
|
917
|
+
// Branch order mirrors emit_engine.ts: workspace-invariant bodies → aggregateInvariant →
|
|
852
918
|
// invariant → derive → combine → report (the gate's most recent dispatch key wins).
|
|
853
919
|
const wsJob = job as {
|
|
854
920
|
intent?: {
|
|
855
|
-
workspaceInvariantList?: unknown;
|
|
856
921
|
workspaceInvariantReads?: { id?: string };
|
|
857
922
|
workspaceInvariant?: { id?: string };
|
|
858
923
|
payload?: unknown;
|
|
859
924
|
};
|
|
860
925
|
priorState?: Record<string, unknown>;
|
|
861
926
|
};
|
|
862
|
-
if (wsJob.intent?.workspaceInvariantList !== undefined) {
|
|
863
|
-
return planWorkspaceInvariantList();
|
|
864
|
-
}
|
|
865
927
|
if (wsJob.intent?.workspaceInvariantReads !== undefined) {
|
|
866
928
|
return planWorkspaceInvariantReads(wsJob);
|
|
867
929
|
}
|
|
@@ -934,7 +996,12 @@ export function registerEngine(config: EngineEntryConfig): RegisteredEngine {
|
|
|
934
996
|
// token qualifies explicitly. (`module.domain` === `intent.domain`.) Synchronous plan execution ⇒
|
|
935
997
|
// cleared in the `finally`.
|
|
936
998
|
const slice = sliceOf(domain)!;
|
|
937
|
-
__setDispatchContext({
|
|
999
|
+
__setDispatchContext({
|
|
1000
|
+
domain,
|
|
1001
|
+
relationNamespace: routing.relationNamespaceOf?.[domain] ?? domain,
|
|
1002
|
+
qualifiable: slice.qualifiableRelations,
|
|
1003
|
+
qualifiableAggregates: slice.qualifiableAggregates,
|
|
1004
|
+
});
|
|
938
1005
|
let wire: ReturnType<typeof executeDirectiveToIntent>;
|
|
939
1006
|
try {
|
|
940
1007
|
wire = executeDirectiveToIntent(entry.directive, entry.agg, intent.payload as never, ctx);
|
|
@@ -952,6 +1019,28 @@ export function registerEngine(config: EngineEntryConfig): RegisteredEngine {
|
|
|
952
1019
|
// `captured_ports.births` and folds it after admit; decode_plan_output ignores it for the byte-compare),
|
|
953
1020
|
// so a birth-free intent still emits the bare event array, byte-identical (the era rule).
|
|
954
1021
|
const births = wire.births;
|
|
1022
|
+
// CURRENT KERNELS expose a Rust-owned plan sink inside the sealed engine. Hand the completed
|
|
1023
|
+
// typed wire values to it while they are still native QuickJS objects, so the kernel can build
|
|
1024
|
+
// and meter `Event`s directly instead of walking a second returned object graph. This is an
|
|
1025
|
+
// execution ABI, not authority: the same values still pass the ordinary kernel gates, and old
|
|
1026
|
+
// kernels/packages retain the returned-value lane below for era replay.
|
|
1027
|
+
const submitNativePlan = (globalThis as unknown as {
|
|
1028
|
+
nomos?: {
|
|
1029
|
+
__submitPlan?: (
|
|
1030
|
+
events: WireEvent[],
|
|
1031
|
+
strikes: string[],
|
|
1032
|
+
endorse: { sink: string; facet: string; reason: string | null }[],
|
|
1033
|
+
births: { workspace: string; genesisChain: unknown }[],
|
|
1034
|
+
) => void;
|
|
1035
|
+
};
|
|
1036
|
+
}).nomos?.__submitPlan;
|
|
1037
|
+
if (typeof submitNativePlan === "function") {
|
|
1038
|
+
submitNativePlan(wire.events, wire.strikes, endorse ?? [], births ?? []);
|
|
1039
|
+
// The Rust sink now owns the result. Return the smallest lawful sentinel; the kernel ignores
|
|
1040
|
+
// it when a native submission is present and reconstructs compatibility bytes from its typed
|
|
1041
|
+
// events. This avoids retaining and re-traversing the large event graph at the ABI boundary.
|
|
1042
|
+
return [];
|
|
1043
|
+
}
|
|
955
1044
|
if (!wire.strikes.length && !(endorse && endorse.length) && !(births && births.length)) return wire.events;
|
|
956
1045
|
return {
|
|
957
1046
|
events: wire.events,
|
|
@@ -972,58 +1061,21 @@ export function registerEngine(config: EngineEntryConfig): RegisteredEngine {
|
|
|
972
1061
|
* package title), matching the compiler's `domainHash`/`"<name> (nomos package)"`.
|
|
973
1062
|
*/
|
|
974
1063
|
function describe(opts?: { lawHash?: string; title?: string }): OpenApiDoc {
|
|
975
|
-
const
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
const sources = config.domains[domainName];
|
|
979
|
-
if (sources === undefined) continue;
|
|
980
|
-
const merged = mergeModules(sources.map((m) => (typeof m === "function" ? m() : m)), domainName);
|
|
981
|
-
// Aggregates dedup by wire id + RelationTuple auto-ensure (the compose contract) — openapi
|
|
982
|
-
// reads aggregates only for the hasMany inverse indexes, so this matches composeDomainModule.
|
|
983
|
-
const byId = aggregatesOf(merged);
|
|
984
|
-
const discoveredDirectives = [...directivesOf(merged).values()];
|
|
985
|
-
const applicationLanguage = applicationLanguageOf([merged], domainName);
|
|
986
|
-
const directives = applicationLanguage === undefined
|
|
987
|
-
? discoveredDirectives
|
|
988
|
-
: executableDirectivesForLanguage(applicationLanguage, discoveredDirectives, domainName);
|
|
989
|
-
const applicationIntents = applicationLanguage === undefined
|
|
990
|
-
? undefined
|
|
991
|
-
: [
|
|
992
|
-
...applicationLanguage.intents,
|
|
993
|
-
...directives.filter((directive) =>
|
|
994
|
-
isBusinessBirthDirective(directive) &&
|
|
995
|
-
!applicationLanguage.intents.some((intent) => intent.id === directive.id)),
|
|
996
|
-
];
|
|
997
|
-
if (!byId.has(RelationTuple.id) && directives.some((d) => d.aggregateId === RelationTuple.id)) {
|
|
998
|
-
byId.set(RelationTuple.id, RelationTuple);
|
|
999
|
-
}
|
|
1000
|
-
const decls = config.openapiDecls?.[domainName] ?? {};
|
|
1001
|
-
const module = {
|
|
1002
|
-
name: domainName,
|
|
1003
|
-
domain: domainName,
|
|
1004
|
-
aggregates: [...byId.values()],
|
|
1005
|
-
directives,
|
|
1006
|
-
...(applicationIntents === undefined ? {} : { applicationIntents }),
|
|
1007
|
-
...(decls.queries && decls.queries.length > 0 ? { queries: [...decls.queries] } : {}),
|
|
1008
|
-
...(decls.counts && decls.counts.length > 0 ? { counts: [...decls.counts] } : {}),
|
|
1009
|
-
...(decls.sums && decls.sums.length > 0 ? { sums: [...decls.sums] } : {}),
|
|
1010
|
-
...(decls.mins && decls.mins.length > 0 ? { mins: [...decls.mins] } : {}),
|
|
1011
|
-
...(decls.maxes && decls.maxes.length > 0 ? { maxes: [...decls.maxes] } : {}),
|
|
1012
|
-
...(decls.spatials && decls.spatials.length > 0 ? { spatials: [...decls.spatials] } : {}),
|
|
1013
|
-
...(decls.workspaceInvariants && decls.workspaceInvariants.length > 0
|
|
1014
|
-
? { workspaceInvariants: [...decls.workspaceInvariants] }
|
|
1015
|
-
: {}),
|
|
1016
|
-
} as unknown as DomainModule;
|
|
1017
|
-
fragments.push(generateOpenApiFragment(module, {
|
|
1018
|
-
...(lawHash !== undefined ? { lawHash } : {}),
|
|
1019
|
-
includeFrameworkRoutes: true,
|
|
1020
|
-
}));
|
|
1064
|
+
const document = config.openapiDocument;
|
|
1065
|
+
if (document === undefined) {
|
|
1066
|
+
throw new Error("engine describe: selected plan has no compiled OpenAPI projection");
|
|
1021
1067
|
}
|
|
1022
|
-
const
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1068
|
+
const lawHash = opts?.lawHash;
|
|
1069
|
+
const title = opts?.title ?? config.packageTitle ?? document.info["title"];
|
|
1070
|
+
const priorInfo = document.info;
|
|
1071
|
+
const info: Record<string, unknown> = {
|
|
1072
|
+
...priorInfo,
|
|
1073
|
+
...(title === undefined ? {} : { title }),
|
|
1074
|
+
version: lawHash === undefined ? (priorInfo["version"] ?? "0") : lawHash.slice(0, 12),
|
|
1075
|
+
};
|
|
1076
|
+
if (lawHash === undefined) delete info["x-nomos-law-hash"];
|
|
1077
|
+
else info["x-nomos-law-hash"] = lawHash;
|
|
1078
|
+
return { ...document, info };
|
|
1027
1079
|
}
|
|
1028
1080
|
|
|
1029
1081
|
// The engine evals the bundle as a classic script and FREEZES globalThis before
|
|
@@ -1031,7 +1083,9 @@ export function registerEngine(config: EngineEntryConfig): RegisteredEngine {
|
|
|
1031
1083
|
// describe verb (the kernel `describe` run mode evals `globalThis.describe(opts)`).
|
|
1032
1084
|
(globalThis as { plan?: unknown }).plan = plan;
|
|
1033
1085
|
(globalThis as { planReport?: unknown }).planReport = planReport;
|
|
1086
|
+
(globalThis as { __nomosPrepareDomain?: unknown }).__nomosPrepareDomain = prepareDomain;
|
|
1087
|
+
(globalThis as { __nomosHasDirective?: unknown }).__nomosHasDirective = hasDirective;
|
|
1034
1088
|
(globalThis as { describe?: unknown }).describe = describe;
|
|
1035
1089
|
|
|
1036
|
-
return { plan, planReport, describe } as RegisteredEngine;
|
|
1090
|
+
return { plan, planReport, prepareDomain, hasDirective, describe } as RegisteredEngine;
|
|
1037
1091
|
}
|
package/src/evolve_preview.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* THE EVOLVE PREVIEW — `githolon check --against <prior>` (architecture/author_time_gate.md,
|
|
8
8
|
* the `--against` row of check slice 3). The evolve gate is the kernel's #58 diff-by-stable-id
|
|
9
9
|
* (executor/src/law58.rs::evolve_violation): a RENAME (same sid, new label) admits silently; a
|
|
10
|
-
* true REMOVAL / merge-driver change refuses TYPED unless the
|
|
10
|
+
* true REMOVAL / merge-driver change refuses TYPED unless the installation offer carries a
|
|
11
11
|
* `dispositions` acknowledgement. A changed BUSINESS VALUE CONTRACT is stricter: it needs
|
|
12
12
|
* deterministic `.evolvesFrom(...)` coverage and cannot be waived by an acknowledgement.
|
|
13
13
|
*
|
|
@@ -38,7 +38,6 @@ import type { StableIdLockfile } from "./stable_id_lockfile.js";
|
|
|
38
38
|
export type { StableIdLockfile } from "./stable_id_lockfile.js";
|
|
39
39
|
import type { DomainModule } from "./codegen_dart.js";
|
|
40
40
|
import type { WireSchema } from "./wire.js";
|
|
41
|
-
import { parseUsdaDocument } from "./usd_layers.js";
|
|
42
41
|
|
|
43
42
|
// ── the normalized law shape both sides of the diff speak ──────────────────────────
|
|
44
43
|
|
|
@@ -72,7 +71,7 @@ export interface EvolveDomain {
|
|
|
72
71
|
/** A whole package's evolve surface, keyed by DOMAIN KEY. */
|
|
73
72
|
export type EvolveLaw = Record<string, EvolveDomain>;
|
|
74
73
|
|
|
75
|
-
/** The dispositions fragment the
|
|
74
|
+
/** The dispositions fragment the installDomain offer carries to admit destructive evolves (the exact
|
|
76
75
|
* shape executor/src/law58.rs::dispositions_from_payload parses). */
|
|
77
76
|
export interface EvolveDispositionsJson {
|
|
78
77
|
retired?: string[];
|
|
@@ -112,7 +111,7 @@ function aggregatesFromStableIds(
|
|
|
112
111
|
return out;
|
|
113
112
|
}
|
|
114
113
|
|
|
115
|
-
/** Build an EvolveDomain from a DomainInterface
|
|
114
|
+
/** Build an EvolveDomain from a diagnostic DomainInterface — its aggregates are
|
|
116
115
|
* already sid-keyed with kinds+labels, its directives carry payload shapes. Drivers are absent
|
|
117
116
|
* from an interface (it exposes only read kinds), so a driver-only retype is not seen from an
|
|
118
117
|
* interface alone — the lockfile lane (aggregatesFromStableIds + schemas) carries them. */
|
|
@@ -132,72 +131,29 @@ function evolveDomainFromInterface(iface: DomainInterface): EvolveDomain {
|
|
|
132
131
|
};
|
|
133
132
|
}
|
|
134
133
|
|
|
135
|
-
/**
|
|
136
|
-
*
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
export function usdJsonFromPackage(bundle: string): unknown | undefined {
|
|
144
|
-
try {
|
|
145
|
-
return parseUsdaDocument(bundle);
|
|
146
|
-
} catch {
|
|
147
|
-
return undefined;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/** Per-domain-key { stableIds, schemas } parsed out of a package's embedded USD-IR (mirrors
|
|
152
|
-
* executor/src/law58.rs::stable_ids_from_usd + the aggregate-prim schema harvest). */
|
|
153
|
-
export function stableIdsFromUsd(
|
|
154
|
-
usd: unknown,
|
|
155
|
-
): Record<string, { stableIds: StableIds; schemas: Record<string, WireSchema> }> {
|
|
156
|
-
const out: Record<string, { stableIds: StableIds; schemas: Record<string, WireSchema> }> = {};
|
|
157
|
-
const layers = (usd as { layers?: unknown[] })?.layers;
|
|
158
|
-
if (!Array.isArray(layers)) return out;
|
|
159
|
-
for (const layer of layers) {
|
|
160
|
-
const l = layer as {
|
|
161
|
-
path?: string;
|
|
162
|
-
nomosStableIds?: StableIds;
|
|
163
|
-
prims?: Array<{ kind?: string; type?: string; schema?: Record<string, unknown> }>;
|
|
164
|
-
};
|
|
165
|
-
const path = typeof l.path === "string" ? l.path : "";
|
|
166
|
-
const key = path.startsWith("/Nomos/") ? path.slice("/Nomos/".length) : "";
|
|
167
|
-
if (key === "" || l.nomosStableIds === undefined) continue;
|
|
168
|
-
const schemas: Record<string, WireSchema> = {};
|
|
169
|
-
for (const prim of l.prims ?? []) {
|
|
170
|
-
if (prim.kind === "aggregate" && typeof prim.type === "string" && prim.schema) {
|
|
171
|
-
schemas[prim.type] = prim.schema as WireSchema;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
out[key] = { stableIds: l.nomosStableIds, schemas };
|
|
175
|
-
}
|
|
176
|
-
return out;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/** Build an EvolveLaw from a deploy.json body: interface for directive payloads + sid-keyed read
|
|
180
|
-
* shapes, and the embedded package USD-IR for the driver schemas (so a driver-only retype is
|
|
181
|
-
* seen from a deploy.json alone). */
|
|
182
|
-
export function evolveLawFromDeployJson(deploy: DeployJson): EvolveLaw {
|
|
134
|
+
/** Build an EvolveLaw from compiler diagnostic sidecars. The interface supplies directive and
|
|
135
|
+
* read shapes; the stable-id lock supplies identity, drivers and value-contract evolution. Neither
|
|
136
|
+
* is deployable law: canonical law remains the composed, logically named USDA closure interpreted
|
|
137
|
+
* by the kernel; `.law.usdz` is only its lossless one-file transport envelope. */
|
|
138
|
+
export function evolveLawFromInterface(
|
|
139
|
+
ifaces: Record<string, DomainInterface>,
|
|
140
|
+
lock?: StableIdLockfile,
|
|
141
|
+
): EvolveLaw {
|
|
183
142
|
const law: EvolveLaw = {};
|
|
184
|
-
const ifaces = deploy.interface ?? {};
|
|
185
|
-
const usd = deploy.packageUsda !== undefined ? usdJsonFromPackage(deploy.packageUsda) : undefined;
|
|
186
|
-
const fromUsd = usd !== undefined ? stableIdsFromUsd(usd) : {};
|
|
187
143
|
for (const [key, iface] of Object.entries(ifaces)) {
|
|
188
144
|
const dom = evolveDomainFromInterface(iface);
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
if (
|
|
145
|
+
const locked = lock?.domains[key] ?? lock?.domains[iface.domain];
|
|
146
|
+
const stableIds = locked?.stableIds;
|
|
147
|
+
if (stableIds !== undefined) {
|
|
192
148
|
for (const agg of Object.values(dom.aggregates)) {
|
|
193
|
-
const stableAggregate = Object.values(
|
|
149
|
+
const stableAggregate = Object.values(stableIds.aggregates)
|
|
194
150
|
.find((candidate) => candidate.sid === agg.sid);
|
|
195
151
|
const stableFields = new Map(
|
|
196
152
|
Object.values(stableAggregate?.fields ?? {})
|
|
197
153
|
.map((field) => [field.sid, field] as const),
|
|
198
154
|
);
|
|
199
|
-
const schema =
|
|
200
|
-
?? Object.entries(
|
|
155
|
+
const schema = locked?.schemas?.[agg.label]
|
|
156
|
+
?? Object.entries(locked?.schemas ?? {}).find(([type]) => type.endsWith(`.${agg.label}`))?.[1];
|
|
201
157
|
for (const f of Object.values(agg.fields)) {
|
|
202
158
|
const driver = schema?.[f.label];
|
|
203
159
|
if (driver !== undefined) (f as { driver?: string }).driver = JSON.stringify(driver);
|
|
@@ -212,10 +168,14 @@ export function evolveLawFromDeployJson(deploy: DeployJson): EvolveLaw {
|
|
|
212
168
|
}
|
|
213
169
|
law[key] = dom;
|
|
214
170
|
}
|
|
215
|
-
// A
|
|
216
|
-
for (const [key,
|
|
171
|
+
// A lockfile without a sibling interface still supports aggregate/field identity checks.
|
|
172
|
+
for (const [key, locked] of Object.entries(lock?.domains ?? {})) {
|
|
217
173
|
if (law[key] !== undefined) continue;
|
|
218
|
-
law[key] = {
|
|
174
|
+
law[key] = {
|
|
175
|
+
domainSid: locked.stableIds?.domain,
|
|
176
|
+
aggregates: aggregatesFromStableIds(locked.stableIds, locked.schemas),
|
|
177
|
+
directives: {},
|
|
178
|
+
};
|
|
219
179
|
}
|
|
220
180
|
return law;
|
|
221
181
|
}
|
|
@@ -275,7 +235,7 @@ export function currentEvolveLaw(
|
|
|
275
235
|
// ── the diff ────────────────────────────────────────────────────────────────────────
|
|
276
236
|
|
|
277
237
|
/** The full preview result: the findings AND the merged dispositions the destructive findings
|
|
278
|
-
* demand, keyed by DOMAIN KEY (
|
|
238
|
+
* demand, keyed by DOMAIN KEY (the object carried by an installDomain offer). */
|
|
279
239
|
export interface EvolveDiffResult {
|
|
280
240
|
readonly findings: Finding[];
|
|
281
241
|
/** domain key → the dispositions fragment that makes THIS domain's destructive evolves admit. */
|
|
@@ -337,7 +297,7 @@ function diffDomain(
|
|
|
337
297
|
domain,
|
|
338
298
|
level: "refuse",
|
|
339
299
|
what: `aggregate '${oldAgg.label}' (stable id ${sid}) is REMOVED — the evolve gate will refuse without a disposition`,
|
|
340
|
-
fix: `
|
|
300
|
+
fix: `include in the installation offer: "dispositions": { "retired": ["${sid}"] }`,
|
|
341
301
|
});
|
|
342
302
|
continue;
|
|
343
303
|
}
|
|
@@ -359,7 +319,7 @@ function diffDomain(
|
|
|
359
319
|
domain,
|
|
360
320
|
level: "refuse",
|
|
361
321
|
what: `field '${oldAgg.label}.${oldF.label}' (stable id ${fSid}) is REMOVED — the evolve gate will refuse without a disposition`,
|
|
362
|
-
fix: `
|
|
322
|
+
fix: `include in the installation offer: "dispositions": { "retired": ["${fSid}"] }`,
|
|
363
323
|
});
|
|
364
324
|
continue;
|
|
365
325
|
}
|
|
@@ -396,7 +356,7 @@ function diffDomain(
|
|
|
396
356
|
domain,
|
|
397
357
|
level: "refuse",
|
|
398
358
|
what: `field '${oldAgg.label}.${oldF.label}' (stable id ${fSid}) is RETYPED in place (${how}) — the evolve gate will refuse without a disposition`,
|
|
399
|
-
fix: `
|
|
359
|
+
fix: `include in the installation offer: "dispositions": { "retyped": { "${fSid}": "<why goes here>" } } — or add a NEW field instead`,
|
|
400
360
|
});
|
|
401
361
|
}
|
|
402
362
|
}
|
|
@@ -492,7 +452,7 @@ export function evolveDiff(prior: EvolveLaw, current: EvolveLaw): EvolveDiffResu
|
|
|
492
452
|
domain,
|
|
493
453
|
level: "refuse",
|
|
494
454
|
what: `domain '${domain}' aggregate '${agg.label}' (stable id ${sid}) disappears — the current build no longer carries domain '${domain}'`,
|
|
495
|
-
fix: `restore the domain, or
|
|
455
|
+
fix: `restore the domain, or include "dispositions": { "retired": ["${sid}"] } in the replacement installation offer`,
|
|
496
456
|
});
|
|
497
457
|
}
|
|
498
458
|
continue;
|
package/src/extremum.ts
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* VALIDATION RULE (manifest-load, not type-level): the `valueField` parameter MUST be
|
|
29
29
|
* an `int`-kind field of the `of`-aggregate. The DSL type system cannot enforce this
|
|
30
30
|
* at the `min/max(id, valueField)` call site (the aggregate is not yet known), but
|
|
31
|
-
*
|
|
31
|
+
* typed USDA projection-law admission performs a no-fallback reject
|
|
32
32
|
* if the field is absent or non-int on the declared aggregate.
|
|
33
33
|
*
|
|
34
34
|
* ORDER-SENSITIVE GUARDRAIL: min/max expose NO `.first`/`.take`/`.orderBy`. min/max
|