@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.
- package/compile_package.mjs +43 -2
- package/compile_proof.d.mts +47 -0
- package/compile_proof.mjs +343 -0
- package/dart/CHANGELOG.md +7 -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 +5504 -1236
- 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 +196 -89
- 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/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 +76 -35
- package/src/clear_directive.ts +19 -8
- package/src/codegen_dart.ts +305 -22
- package/src/codegen_dart_application.ts +844 -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 +1866 -597
- package/src/compose.ts +1 -1
- package/src/directive.ts +208 -15
- package/src/engine_entry.ts +165 -111
- package/src/evolve_preview.ts +43 -81
- 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 +61 -31
- 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 +278 -0
- package/src/stable_ids.ts +7 -2
- 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/index.ts
CHANGED
|
@@ -9,6 +9,14 @@
|
|
|
9
9
|
// ONE import for a domain file: `z` (payload schemas) rides along with the DSL, so
|
|
10
10
|
// authors never juggle a separate zod dependency (or risk a second zod instance).
|
|
11
11
|
export { z } from "zod";
|
|
12
|
+
export {
|
|
13
|
+
definePackage,
|
|
14
|
+
validateNomosPackageConfig,
|
|
15
|
+
type NomosPackageConfig,
|
|
16
|
+
type NomosPackageDefinition,
|
|
17
|
+
type NomosPackageDomainConfig,
|
|
18
|
+
} from "./package_config.js";
|
|
19
|
+
export type { RuntimePackageDependency } from "./package_dependencies.js";
|
|
12
20
|
export {
|
|
13
21
|
aggregate,
|
|
14
22
|
instance,
|
|
@@ -41,6 +49,10 @@ export {
|
|
|
41
49
|
jsonWithNullSchema,
|
|
42
50
|
type EvidenceRefValue,
|
|
43
51
|
evidenceRefSchema,
|
|
52
|
+
type Sha256Digest,
|
|
53
|
+
sha256Digest,
|
|
54
|
+
isSha256DigestSchema,
|
|
55
|
+
NOMOS_SHA256_DIGEST_BRAND,
|
|
44
56
|
// FIELD FACETS (the endorse model — security_model.md): authority/standing/attribution gate
|
|
45
57
|
// set() acceptance of PrincipalEvidence; secret/public are the confidentiality lattice.
|
|
46
58
|
authorityField,
|
|
@@ -61,11 +73,15 @@ export {
|
|
|
61
73
|
idOf,
|
|
62
74
|
idOfMetaOf,
|
|
63
75
|
idOfTargetKindOf,
|
|
76
|
+
versionedRefOf,
|
|
77
|
+
versionedRefOfMetaOf,
|
|
64
78
|
brandedId,
|
|
65
79
|
brandedIdMetaOf,
|
|
66
80
|
voMetaOf,
|
|
67
81
|
voSample,
|
|
68
82
|
NOMOS_VO_BRAND,
|
|
83
|
+
NOMOS_VERSIONED_REF_BRAND,
|
|
84
|
+
type VersionedRefOfMeta,
|
|
69
85
|
geo,
|
|
70
86
|
type ValueObject,
|
|
71
87
|
type ValueObjectValue,
|
|
@@ -478,6 +494,7 @@ export {
|
|
|
478
494
|
recordShare,
|
|
479
495
|
pruneShare,
|
|
480
496
|
sharedByWorkspace,
|
|
497
|
+
sharedByObject,
|
|
481
498
|
ReceivingGroup,
|
|
482
499
|
ReceivingGroupPolicy,
|
|
483
500
|
RECEIVING_GROUP_ID,
|
|
@@ -594,9 +611,7 @@ export {
|
|
|
594
611
|
} from "./framework/capability.js";
|
|
595
612
|
export {
|
|
596
613
|
DOMAIN_INSTALLATION_PHASES,
|
|
597
|
-
PolicyBundle,
|
|
598
614
|
DomainInstallation,
|
|
599
|
-
policyAggregateId,
|
|
600
615
|
domainInstallationAggregateId,
|
|
601
616
|
installDomain,
|
|
602
617
|
stageDomain,
|
|
@@ -623,8 +638,18 @@ export {
|
|
|
623
638
|
} from "./framework/repair.js";
|
|
624
639
|
export {
|
|
625
640
|
workspaceInvariant,
|
|
641
|
+
readRef,
|
|
642
|
+
readSet,
|
|
643
|
+
idItem,
|
|
644
|
+
idLiteral,
|
|
645
|
+
idPayload,
|
|
646
|
+
idTemplate,
|
|
626
647
|
ref,
|
|
627
648
|
refAs,
|
|
649
|
+
type InvariantReadBinding,
|
|
650
|
+
type InvariantReadId,
|
|
651
|
+
type InvariantReadIdPart,
|
|
652
|
+
type InvariantReadTransform,
|
|
628
653
|
type WorkspaceInvariantDecl,
|
|
629
654
|
type WorkspaceInvariantVerdict,
|
|
630
655
|
type WorkspaceInvariantReads,
|
|
@@ -729,7 +754,10 @@ export {
|
|
|
729
754
|
type InvariantEvidence,
|
|
730
755
|
type InvariantVerdict,
|
|
731
756
|
} from "./relation.js";
|
|
732
|
-
|
|
757
|
+
// Build-time generators live on explicit package subpaths. Keep the structural type available to
|
|
758
|
+
// authoring tools, but never pull generator implementations into the runtime namespace evaluated by
|
|
759
|
+
// every kernel sandbox.
|
|
760
|
+
export type { DomainModule } from "./codegen_dart.js";
|
|
733
761
|
// SHAPE COMPATIBILITY (#72) — the structural contract a client negotiates with a workspace
|
|
734
762
|
// by, instead of the law hash. Pure + deterministic; the editor/CI mirror imports these.
|
|
735
763
|
export {
|
|
@@ -744,28 +772,20 @@ export {
|
|
|
744
772
|
// turned on by the `--emit-dot` compiler flag). Pure `DomainModule -> string`, deterministic;
|
|
745
773
|
// NOT part of the domain identity. Barrel-safe (no node:* imports), so the engine bundle can
|
|
746
774
|
// import this barrel unaffected.
|
|
747
|
-
export {
|
|
775
|
+
export type { DotOptions } from "./codegen_dot.js";
|
|
748
776
|
// The OPTIONAL OpenUSD `.usda` emitter — a COMPILED DOMAIN as a navigable USD stage (aggregates →
|
|
749
777
|
// Sphere prims, directives → Cube prims carrying their `plan` lump, the creates/mutates/ref flow →
|
|
750
778
|
// BasisCurves). The abstract LAW structure as a maths object (holon-maths projection), NOT the org-data
|
|
751
779
|
// twin. Sibling of `emitDomainDot`: pure `DomainModule -> string`, deterministic, NOT part of the
|
|
752
780
|
// domain identity; barrel-safe (no node:* imports). Open the output in usdview / any USD tool.
|
|
753
|
-
export {
|
|
781
|
+
export type { UsdaOptions } from "./codegen_usda.js";
|
|
754
782
|
// The PROVIDER SDK codegen (symmetric twin of the peer payload classes). `emitProviderSdk`
|
|
755
783
|
// is invoked INSIDE `generateDartDomain` for a module's `impureCapabilities`; the type is exported
|
|
756
784
|
// so the emit script can type its `impureCapabilities` list. A `impureCapability(...)` factory return value
|
|
757
785
|
// satisfies `ImpureCapabilityDecl` structurally.
|
|
758
|
-
export {
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
taskBaseName,
|
|
762
|
-
type ImpureCapabilityDecl,
|
|
763
|
-
} from "./codegen_provider_dart.js";
|
|
764
|
-
// NOTE: the canonical-manifest / domain-hash API (`./manifest.js`) is a BUILD-TIME
|
|
765
|
-
// concern that imports `node:crypto`. It is DELIBERATELY NOT re-exported from this
|
|
766
|
-
// barrel: the deterministic engine bundle (`golden/domain_nomos.js`) imports this
|
|
767
|
-
// barrel, and esbuild `--platform=neutral` cannot resolve `node:crypto`. Build-time
|
|
768
|
-
// consumers import it via the `@githolon/dsl/manifest` subpath instead.
|
|
786
|
+
export type { ImpureCapabilityDecl } from "./codegen_provider_dart.js";
|
|
787
|
+
// Canonical law is emitted as typed USDA. The retired canonical-JSON manifest helper has
|
|
788
|
+
// been removed from both this barrel and the package exports.
|
|
769
789
|
export { encodeKernelSchema, executeDirectiveToIntent, encodeRefMode, intentIdFromHlc } from "./wire_encode.js";
|
|
770
790
|
export type {
|
|
771
791
|
WireEvent,
|
package/src/intent.ts
CHANGED
|
@@ -18,7 +18,12 @@ import type { Ports } from "./ctx.js";
|
|
|
18
18
|
import type { PlannedOp } from "./ops.js";
|
|
19
19
|
import type { Field } from "./fields.js";
|
|
20
20
|
import type { AggregateHandle } from "./aggregate.js";
|
|
21
|
-
import type {
|
|
21
|
+
import type {
|
|
22
|
+
InvariantReadBinding,
|
|
23
|
+
InvariantReadId,
|
|
24
|
+
InvariantReadIdPart,
|
|
25
|
+
WorkspaceInvariantDecl,
|
|
26
|
+
} from "./framework/workspace_invariant.js";
|
|
22
27
|
import { businessBirthConsequence, type BusinessBirthDirective } from "./business_birth.js";
|
|
23
28
|
import type { BusinessBirthDecl } from "./births.js";
|
|
24
29
|
import type { WorkspaceSharingPolicy } from "./workspace_sharing.js";
|
|
@@ -31,6 +36,132 @@ export interface IntentStep<I = unknown, P = unknown> {
|
|
|
31
36
|
readonly payload: (facts: I) => P;
|
|
32
37
|
}
|
|
33
38
|
|
|
39
|
+
const PAYLOAD_TRACE_PATH = Symbol("nomos.intent.payload-trace-path");
|
|
40
|
+
|
|
41
|
+
type PayloadTrace = {
|
|
42
|
+
readonly [PAYLOAD_TRACE_PATH]: readonly string[];
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* An inert symbolic business payload used only while compiling application-language
|
|
47
|
+
* composition. A private payload mapping may carry a public field through unchanged;
|
|
48
|
+
* in that case the trace proves the exact public path without parsing JavaScript source.
|
|
49
|
+
* Any computation/coercion loses the trace and therefore cannot be miscompiled as a
|
|
50
|
+
* native read recipe.
|
|
51
|
+
*/
|
|
52
|
+
function payloadTrace(path: readonly string[] = []): PayloadTrace {
|
|
53
|
+
const target = Object.create(null) as PayloadTrace;
|
|
54
|
+
return new Proxy(target, {
|
|
55
|
+
get(_target, property) {
|
|
56
|
+
if (property === PAYLOAD_TRACE_PATH) return path;
|
|
57
|
+
if (property === "then") return undefined;
|
|
58
|
+
if (property === Symbol.toPrimitive || property === "toString" || property === "valueOf") {
|
|
59
|
+
return () => {
|
|
60
|
+
throw new Error("computed application-language payload field is not a direct path");
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
return payloadTrace([...path, String(property)]);
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function tracedPath(value: unknown): readonly string[] | undefined {
|
|
69
|
+
if (value === null || typeof value !== "object") return undefined;
|
|
70
|
+
const path = (value as Partial<PayloadTrace>)[PAYLOAD_TRACE_PATH];
|
|
71
|
+
return Array.isArray(path) ? path : undefined;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function valueAtPath(value: unknown, path: readonly string[]): unknown {
|
|
75
|
+
let current = value;
|
|
76
|
+
for (const part of path) {
|
|
77
|
+
if (current === null || (typeof current !== "object" && typeof current !== "function")) {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
current = (current as Record<string, unknown>)[part];
|
|
81
|
+
}
|
|
82
|
+
return current;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function remapPayloadPath(
|
|
86
|
+
mapped: unknown,
|
|
87
|
+
privatePath: readonly string[],
|
|
88
|
+
): readonly string[] | undefined {
|
|
89
|
+
return tracedPath(valueAtPath(mapped, privatePath));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function remapStepPayloadPath(
|
|
93
|
+
step: IntentStep<any, any>,
|
|
94
|
+
privatePath: readonly string[],
|
|
95
|
+
): readonly string[] | undefined {
|
|
96
|
+
try {
|
|
97
|
+
return remapPayloadPath(step.payload(payloadTrace()), privatePath);
|
|
98
|
+
} catch {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function remapReadPart(
|
|
104
|
+
part: InvariantReadIdPart,
|
|
105
|
+
mapped: unknown,
|
|
106
|
+
): InvariantReadIdPart | undefined {
|
|
107
|
+
if (part.source !== "payload") return part;
|
|
108
|
+
const path = remapPayloadPath(mapped, part.path);
|
|
109
|
+
return path === undefined ? undefined : { ...part, path };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function remapReadId(id: InvariantReadId, mapped: unknown): InvariantReadId | undefined {
|
|
113
|
+
if (id.source === "literal") return id;
|
|
114
|
+
if (id.source === "payload") {
|
|
115
|
+
const path = remapPayloadPath(mapped, id.path);
|
|
116
|
+
return path === undefined ? undefined : { ...id, path };
|
|
117
|
+
}
|
|
118
|
+
const parts: InvariantReadIdPart[] = [];
|
|
119
|
+
for (const part of id.parts) {
|
|
120
|
+
const remapped = remapReadPart(part, mapped);
|
|
121
|
+
if (remapped === undefined) return undefined;
|
|
122
|
+
parts.push(remapped);
|
|
123
|
+
}
|
|
124
|
+
return { ...id, parts };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Rewrite a private effect's typed point-read recipe onto its public business facts.
|
|
128
|
+
* Returns undefined unless every payload dependency is structurally proven. */
|
|
129
|
+
function remapNativeReads(
|
|
130
|
+
reads: readonly InvariantReadBinding[],
|
|
131
|
+
step: IntentStep<any, any>,
|
|
132
|
+
): readonly InvariantReadBinding[] | undefined {
|
|
133
|
+
const needsPayload = reads.some((read) =>
|
|
134
|
+
read.eachPayloadPath !== undefined ||
|
|
135
|
+
read.id.source === "payload" ||
|
|
136
|
+
(read.id.source === "template" && read.id.parts.some((part) => part.source === "payload"))
|
|
137
|
+
);
|
|
138
|
+
if (!needsPayload) return reads;
|
|
139
|
+
|
|
140
|
+
let mapped: unknown;
|
|
141
|
+
try {
|
|
142
|
+
mapped = step.payload(payloadTrace());
|
|
143
|
+
} catch {
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const out: InvariantReadBinding[] = [];
|
|
148
|
+
for (const read of reads) {
|
|
149
|
+
const eachPayloadPath = read.eachPayloadPath === undefined
|
|
150
|
+
? undefined
|
|
151
|
+
: remapPayloadPath(mapped, read.eachPayloadPath);
|
|
152
|
+
if (read.eachPayloadPath !== undefined && eachPayloadPath === undefined) return undefined;
|
|
153
|
+
|
|
154
|
+
const id = remapReadId(read.id, mapped);
|
|
155
|
+
if (id === undefined) return undefined;
|
|
156
|
+
out.push({
|
|
157
|
+
...read,
|
|
158
|
+
...(eachPayloadPath === undefined ? {} : { eachPayloadPath }),
|
|
159
|
+
id,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
return out;
|
|
163
|
+
}
|
|
164
|
+
|
|
34
165
|
export interface BusinessIntent<I = unknown> extends Directive<I> {
|
|
35
166
|
readonly [INTENT_BRAND]: true;
|
|
36
167
|
readonly steps: readonly IntentStep<I, any>[];
|
|
@@ -235,10 +366,33 @@ class IntentBuilder<I> {
|
|
|
235
366
|
}
|
|
236
367
|
|
|
237
368
|
const preconditions: PreconditionDescriptor[] = this.steps.flatMap((step) =>
|
|
238
|
-
(step.directive.preconditions ?? []).map((condition) =>
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
369
|
+
(step.directive.preconditions ?? []).map((condition) => {
|
|
370
|
+
const {
|
|
371
|
+
idPayloadPath: privateIdPayloadPath,
|
|
372
|
+
readId: privateReadId,
|
|
373
|
+
...rest
|
|
374
|
+
} = condition;
|
|
375
|
+
const publicIdPayloadPath = privateIdPayloadPath === undefined
|
|
376
|
+
? undefined
|
|
377
|
+
: remapStepPayloadPath(step, privateIdPayloadPath);
|
|
378
|
+
let publicReadId: InvariantReadId | undefined;
|
|
379
|
+
if (privateReadId !== undefined) {
|
|
380
|
+
try {
|
|
381
|
+
publicReadId = remapReadId(
|
|
382
|
+
privateReadId,
|
|
383
|
+
step.payload(payloadTrace() as unknown as I),
|
|
384
|
+
);
|
|
385
|
+
} catch {
|
|
386
|
+
publicReadId = undefined;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return {
|
|
390
|
+
...rest,
|
|
391
|
+
...(publicIdPayloadPath === undefined ? {} : { idPayloadPath: publicIdPayloadPath }),
|
|
392
|
+
...(publicReadId === undefined ? {} : { readId: publicReadId }),
|
|
393
|
+
idField: (facts: unknown) => condition.idField(step.payload(facts as I)),
|
|
394
|
+
};
|
|
395
|
+
}),
|
|
242
396
|
);
|
|
243
397
|
const queryReads = dedupeBy(
|
|
244
398
|
this.steps.flatMap((step) => step.directive.declaredQueryReads ?? []),
|
|
@@ -289,6 +443,14 @@ class IntentBuilder<I> {
|
|
|
289
443
|
},
|
|
290
444
|
marker: primary.marker as ReferentialMarker,
|
|
291
445
|
aggregateId: primary.aggregateId,
|
|
446
|
+
// Keep the typed local target on the lowered public intent. A narrow
|
|
447
|
+
// runtime plan may retain only this intent export; without the handle the
|
|
448
|
+
// engine cannot rebuild its aggregate registry entry and silently drops
|
|
449
|
+
// compound intents that also birth a child.
|
|
450
|
+
...(primary.aggregate === undefined ? {} : { aggregate: primary.aggregate }),
|
|
451
|
+
...(primary.outputCardinality !== undefined
|
|
452
|
+
? { outputCardinality: primary.outputCardinality }
|
|
453
|
+
: {}),
|
|
292
454
|
...(secondaryOutputs.length > 0 ? { secondaryOutputs } : {}),
|
|
293
455
|
payloadSchema: (birthSteps[0]?.directive.payloadSchema ?? this.contractDeclaration.accepts) as unknown as z.ZodType<I>,
|
|
294
456
|
plan,
|
|
@@ -571,10 +733,20 @@ export function remapWorkspaceInvariantsForLanguage(
|
|
|
571
733
|
// still validates every mapped consequence before it can commit.
|
|
572
734
|
return (parsed.success ? parsed.data : mapped) as Record<string, unknown>;
|
|
573
735
|
};
|
|
736
|
+
// Never carry a private payload path blindly onto a public intent. Prove the
|
|
737
|
+
// mapping structurally with inert path values; otherwise leave the selector
|
|
738
|
+
// compiler-visible so the native-only gate can name the exact unmapped law.
|
|
739
|
+
const remappedNativeReads = invariant.__nomosReadBindings === undefined
|
|
740
|
+
? undefined
|
|
741
|
+
: remapNativeReads(invariant.__nomosReadBindings, use.step);
|
|
742
|
+
const { __nomosReadBindings: _privateReads, ...withoutPrivateReads } = invariant;
|
|
574
743
|
out.push({
|
|
575
|
-
...
|
|
744
|
+
...withoutPrivateReads,
|
|
576
745
|
id: `${use.intent.id}__${invariant.id}`,
|
|
577
746
|
on: use.intent.id,
|
|
747
|
+
...(remappedNativeReads === undefined
|
|
748
|
+
? {}
|
|
749
|
+
: { __nomosReadBindings: remappedNativeReads }),
|
|
578
750
|
reads: (ctx) => invariant.reads({ ...ctx, intent: mapFacts(ctx.intent) }),
|
|
579
751
|
assert: (snapshots, ctx) => invariant.assert(
|
|
580
752
|
snapshots,
|
package/src/law_check.ts
CHANGED
|
@@ -443,7 +443,9 @@ function unreadPayloadFindings(domain: string, dir: Directive<unknown>, probe: P
|
|
|
443
443
|
// ROUTING lane consumes (the plan may pass it straight to read(…, {from}) without a tracked property
|
|
444
444
|
// read on the sampled path) — a declared trust-edge field is never a dead field.
|
|
445
445
|
for (const e of (dir as { declaredForeignReads?: ForeignReadEdge[] }).declaredForeignReads ?? []) {
|
|
446
|
-
if ("fromPayload" in e && typeof e.fromPayload === "string")
|
|
446
|
+
if ("fromPayload" in e && typeof e.fromPayload === "string") {
|
|
447
|
+
gateConsumed.add(e.fromPayload.split(".")[0]!);
|
|
448
|
+
}
|
|
447
449
|
}
|
|
448
450
|
const out: Finding[] = [];
|
|
449
451
|
for (const f of requiredPayloadFields(dir.payloadSchema as z.ZodTypeAny)) {
|
|
@@ -1077,10 +1079,14 @@ function topLevelPortReadFindings(mod: DomainModule, portReadingDomains: Readonl
|
|
|
1077
1079
|
// (b) a trustPolicy whose allow-list aggregate has no directive writing it → WARN (unpopulatable);
|
|
1078
1080
|
// (c) a query declared BOTH as a local .reads() premise AND a foreign policy read → REFUSE (laundering).
|
|
1079
1081
|
|
|
1080
|
-
/** The FOREIGN query ids a directive's plan actually reads
|
|
1081
|
-
*
|
|
1082
|
-
*
|
|
1083
|
-
|
|
1082
|
+
/** The local and FOREIGN query ids a directive's plan actually reads on the one probe sample —
|
|
1083
|
+
* `undefined` when the plan can't be probed (unsynthesizable payload / it threw). This semantic
|
|
1084
|
+
* check is deliberately independent of bundle layout: moving the DSL runtime into an authenticated
|
|
1085
|
+
* shared USDA base must never make an undeclared read disappear from author-time diagnostics. */
|
|
1086
|
+
function readsPerformed(
|
|
1087
|
+
dir: Directive<unknown>,
|
|
1088
|
+
where: string,
|
|
1089
|
+
): { readonly local: Set<string>; readonly foreign: Set<string> } | undefined {
|
|
1084
1090
|
let synth: SynthesizedPayload;
|
|
1085
1091
|
try {
|
|
1086
1092
|
synth = synthesizeDirectivePayload(dir, where);
|
|
@@ -1089,7 +1095,8 @@ function foreignReadsPerformed(dir: Directive<unknown>, where: string): Set<stri
|
|
|
1089
1095
|
}
|
|
1090
1096
|
const payload: Record<string, unknown> = { ...synth.value };
|
|
1091
1097
|
for (const f of synth.autoStamped) payload[f] = "2026-01-01T00:00:00Z";
|
|
1092
|
-
const
|
|
1098
|
+
const local = new Set<string>();
|
|
1099
|
+
const foreign = new Set<string>();
|
|
1093
1100
|
let ok = true;
|
|
1094
1101
|
withBirthProbeGlobals(() => {
|
|
1095
1102
|
const g = globalThis as unknown as { nomos: Record<string, unknown> };
|
|
@@ -1107,7 +1114,8 @@ function foreignReadsPerformed(dir: Directive<unknown>, where: string): Set<stri
|
|
|
1107
1114
|
try {
|
|
1108
1115
|
dir.plan(payload as never, deterministicPorts({ physical: 1, replica: 1, principal: "principal:probe" }));
|
|
1109
1116
|
for (const r of __drainReads()) {
|
|
1110
|
-
if ((r as { attestation?: unknown }).attestation !== undefined)
|
|
1117
|
+
if ((r as { attestation?: unknown }).attestation !== undefined) foreign.add(r.queryId);
|
|
1118
|
+
else local.add(r.queryId);
|
|
1111
1119
|
}
|
|
1112
1120
|
} catch {
|
|
1113
1121
|
ok = false;
|
|
@@ -1117,7 +1125,7 @@ function foreignReadsPerformed(dir: Directive<unknown>, where: string): Set<stri
|
|
|
1117
1125
|
g.nomos.read = prevRead;
|
|
1118
1126
|
}
|
|
1119
1127
|
});
|
|
1120
|
-
return ok ?
|
|
1128
|
+
return ok ? { local, foreign } : undefined;
|
|
1121
1129
|
}
|
|
1122
1130
|
|
|
1123
1131
|
function attestedReadFindings(mod: DomainModule): Finding[] {
|
|
@@ -1158,10 +1166,23 @@ function attestedReadFindings(mod: DomainModule): Finding[] {
|
|
|
1158
1166
|
}
|
|
1159
1167
|
}
|
|
1160
1168
|
|
|
1161
|
-
|
|
1162
|
-
const performed = foreignReadsPerformed(dir, `${domain}/${dir.id}`);
|
|
1169
|
+
const performed = readsPerformed(dir, `${domain}/${dir.id}`);
|
|
1163
1170
|
if (performed !== undefined) {
|
|
1164
|
-
|
|
1171
|
+
// An ordinary captured read is a declared premise, not an incidental function call. Catch it
|
|
1172
|
+
// against the directive itself so package slicing/runtime-base changes cannot hide the error.
|
|
1173
|
+
for (const qid of performed.local) {
|
|
1174
|
+
if (!localQueryIds.has(qid)) {
|
|
1175
|
+
findings.push({
|
|
1176
|
+
domain,
|
|
1177
|
+
directiveId: dir.id,
|
|
1178
|
+
level: "refuse",
|
|
1179
|
+
what: `directive '${dir.id}' references read() for query '${qid}', but NO directive declares a captured-read query for that premise`,
|
|
1180
|
+
fix: `declare the read on this directive: .reads(theQueryDecl) (the same QueryDecl passed to read())`,
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
// (a) an undeclared foreign read — the plan routes read(q, …, { from }) with no trust edge for q.
|
|
1185
|
+
for (const qid of performed.foreign) {
|
|
1165
1186
|
if (!declaredQueryIds.has(qid)) {
|
|
1166
1187
|
findings.push({
|
|
1167
1188
|
domain,
|