@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
|
@@ -13,7 +13,7 @@ import { Lww } from "../drivers.js";
|
|
|
13
13
|
import { set, withMarker } from "../ops.js";
|
|
14
14
|
import { query } from "../query.js";
|
|
15
15
|
import { GENESIS_WARRANT_ID } from "./genesis.js";
|
|
16
|
-
import { refAs, workspaceInvariant } from "./workspace_invariant.js";
|
|
16
|
+
import { readRef, readSet, refAs, workspaceInvariant } from "./workspace_invariant.js";
|
|
17
17
|
|
|
18
18
|
const epochMs = z.number().int().nonnegative();
|
|
19
19
|
|
|
@@ -135,7 +135,7 @@ export const seedAuthProvider = directive("seedAuthProvider")
|
|
|
135
135
|
|
|
136
136
|
export const authProviderSeedBeforeWarrant = workspaceInvariant("AuthProviderSeedBeforeWarrant")
|
|
137
137
|
.on("seedAuthProvider")
|
|
138
|
-
.reads((
|
|
138
|
+
.reads(readSet(readRef("warrant", "GenesisWarrant").at(GENESIS_WARRANT_ID)))
|
|
139
139
|
.assert(({ warrant }) => {
|
|
140
140
|
// Missing invariant refs are represented as an empty snapshot by the executor, not necessarily null.
|
|
141
141
|
const principal = (warrant as { principal?: unknown } | null | undefined)?.principal;
|
|
@@ -27,7 +27,7 @@ import { z } from "zod";
|
|
|
27
27
|
import { instance } from "../aggregate.js";
|
|
28
28
|
import { directive } from "../directive.js";
|
|
29
29
|
import { set, type PlannedOp } from "../ops.js";
|
|
30
|
-
import { refAs, workspaceInvariant } from "./workspace_invariant.js";
|
|
30
|
+
import { readRef, readSet, refAs, workspaceInvariant } from "./workspace_invariant.js";
|
|
31
31
|
import {
|
|
32
32
|
BirthPolicy,
|
|
33
33
|
BirthGrant,
|
|
@@ -149,9 +149,7 @@ export const reclaimChildAsAdmin = directive("reclaimChildAsAdmin")
|
|
|
149
149
|
/** Reclaim is two-phase; a child must first be explicitly withdrawn. */
|
|
150
150
|
export const reclaimChildRequiresRetired = workspaceInvariant("ReclaimChildRequiresRetired")
|
|
151
151
|
.on("reclaimChild")
|
|
152
|
-
.reads((
|
|
153
|
-
refAs("record", "NomosChildLifecycle", "pre:" + childLifecycleId(String(ctx.intent.child ?? ""))),
|
|
154
|
-
])
|
|
152
|
+
.reads(readSet(readRef("record", NomosChildLifecycle).pre().fromPayload("child", { prefix: "nomos-child-lifecycle:" })))
|
|
155
153
|
.assert(({ record }, ctx) =>
|
|
156
154
|
(record as { status?: string; principal?: string } | undefined)?.status === "retired"
|
|
157
155
|
&& sameLifecyclePrincipal((record as { principal?: string }).principal, ctx?.principal)
|
|
@@ -162,9 +160,7 @@ export const reclaimChildRequiresRetired = workspaceInvariant("ReclaimChildRequi
|
|
|
162
160
|
/** Operator recovery has the same two-phase law, without depending on the broken creator credential. */
|
|
163
161
|
export const reclaimChildAsAdminRequiresRetired = workspaceInvariant("ReclaimChildAsAdminRequiresRetired")
|
|
164
162
|
.on("reclaimChildAsAdmin")
|
|
165
|
-
.reads((
|
|
166
|
-
refAs("record", "NomosChildLifecycle", "pre:" + childLifecycleId(String(ctx.intent.child ?? ""))),
|
|
167
|
-
])
|
|
163
|
+
.reads(readSet(readRef("record", NomosChildLifecycle).pre().fromPayload("child", { prefix: "nomos-child-lifecycle:" })))
|
|
168
164
|
.assert(({ record }) =>
|
|
169
165
|
(record as { status?: string } | undefined)?.status === "retired"
|
|
170
166
|
? { accept: true }
|
|
@@ -174,9 +170,7 @@ export const reclaimChildAsAdminRequiresRetired = workspaceInvariant("ReclaimChi
|
|
|
174
170
|
/** Reclaimed is permanent: retirement cannot move a child back out of the terminal state. */
|
|
175
171
|
export const retireChildNotReclaimed = workspaceInvariant("RetireChildNotReclaimed")
|
|
176
172
|
.on("retireChild")
|
|
177
|
-
.reads((
|
|
178
|
-
refAs("record", "NomosChildLifecycle", "pre:" + childLifecycleId(String(ctx.intent.child ?? ""))),
|
|
179
|
-
])
|
|
173
|
+
.reads(readSet(readRef("record", NomosChildLifecycle).pre().fromPayload("child", { prefix: "nomos-child-lifecycle:" })))
|
|
180
174
|
.assert(({ record }) =>
|
|
181
175
|
(record as { status?: string } | undefined)?.status === "reclaimed"
|
|
182
176
|
? { reject: "reclaimed-child-is-terminal" }
|
|
@@ -186,9 +180,7 @@ export const retireChildNotReclaimed = workspaceInvariant("RetireChildNotReclaim
|
|
|
186
180
|
/** Reclaimed is terminal for operator recovery too. */
|
|
187
181
|
export const retireChildAsAdminNotReclaimed = workspaceInvariant("RetireChildAsAdminNotReclaimed")
|
|
188
182
|
.on("retireChildAsAdmin")
|
|
189
|
-
.reads((
|
|
190
|
-
refAs("record", "NomosChildLifecycle", "pre:" + childLifecycleId(String(ctx.intent.child ?? ""))),
|
|
191
|
-
])
|
|
183
|
+
.reads(readSet(readRef("record", NomosChildLifecycle).pre().fromPayload("child", { prefix: "nomos-child-lifecycle:" })))
|
|
192
184
|
.assert(({ record }) =>
|
|
193
185
|
(record as { status?: string } | undefined)?.status === "reclaimed"
|
|
194
186
|
? { reject: "reclaimed-child-is-terminal" }
|
|
@@ -198,9 +190,7 @@ export const retireChildAsAdminNotReclaimed = workspaceInvariant("RetireChildAsA
|
|
|
198
190
|
/** A lifecycle cannot mint authority for arbitrary custody: retirement must name its exact birth witness. */
|
|
199
191
|
export const retireChildRequiresBirth = workspaceInvariant("RetireChildRequiresBirth")
|
|
200
192
|
.on("retireChild")
|
|
201
|
-
.reads((
|
|
202
|
-
refAs("birth", "NomosBirthRecord", "pre:" + birthRecordId(String(ctx.intent.child ?? ""))),
|
|
203
|
-
])
|
|
193
|
+
.reads(readSet(readRef("birth", NomosBirthRecord).pre().fromPayload("child", { prefix: "nomos-birth-record:" })))
|
|
204
194
|
.assert(({ birth }, ctx) => {
|
|
205
195
|
const record = birth as { child?: string; principal?: string } | undefined;
|
|
206
196
|
const intent = ctx?.intent as { child?: string; principal?: string } | undefined;
|
|
@@ -214,9 +204,7 @@ export const retireChildRequiresBirth = workspaceInvariant("RetireChildRequiresB
|
|
|
214
204
|
/** Admin recovery may act for the recorded creator, but still cannot invent or retarget custody. */
|
|
215
205
|
export const retireChildAsAdminRequiresBirth = workspaceInvariant("RetireChildAsAdminRequiresBirth")
|
|
216
206
|
.on("retireChildAsAdmin")
|
|
217
|
-
.reads((
|
|
218
|
-
refAs("birth", "NomosBirthRecord", "pre:" + birthRecordId(String(ctx.intent.child ?? ""))),
|
|
219
|
-
])
|
|
207
|
+
.reads(readSet(readRef("birth", NomosBirthRecord).pre().fromPayload("child", { prefix: "nomos-birth-record:" })))
|
|
220
208
|
.assert(({ birth }, ctx) => {
|
|
221
209
|
const record = birth as { child?: string; principal?: string } | undefined;
|
|
222
210
|
const intent = ctx?.intent as { child?: string; principal?: string } | undefined;
|
|
@@ -22,8 +22,16 @@ const correctionRecord = aggregate(BUSINESS_CORRECTION_AGGREGATE, {
|
|
|
22
22
|
correctedAt: t.string().merge(Lww),
|
|
23
23
|
}).public();
|
|
24
24
|
|
|
25
|
+
export const businessCorrectionRecord = {
|
|
26
|
+
...correctionRecord,
|
|
27
|
+
businessBirthInternal: true as const,
|
|
28
|
+
};
|
|
29
|
+
|
|
25
30
|
const correction = directive(BUSINESS_CORRECTION_DIRECTIVE)
|
|
26
|
-
.
|
|
31
|
+
// The directive's typed aggregate handle is followed by composeDomainModule. It must be the same
|
|
32
|
+
// framework-private singleton that is exported below; pointing at the unbranded construction
|
|
33
|
+
// intermediate lets that handle overwrite the branded one during typed target closure.
|
|
34
|
+
.ensures(businessCorrectionRecord)
|
|
27
35
|
.payload(z.object({
|
|
28
36
|
recordId: z.string().min(1),
|
|
29
37
|
target: z.string().min(1),
|
|
@@ -45,10 +53,6 @@ const correction = directive(BUSINESS_CORRECTION_DIRECTIVE)
|
|
|
45
53
|
];
|
|
46
54
|
});
|
|
47
55
|
|
|
48
|
-
export const businessCorrectionRecord = {
|
|
49
|
-
...correctionRecord,
|
|
50
|
-
businessBirthInternal: true as const,
|
|
51
|
-
};
|
|
52
56
|
export const recordBusinessCorrection = {
|
|
53
57
|
...correction,
|
|
54
58
|
businessBirthInternal: true as const,
|
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
* The `nomos` lifecycle/controller surface.
|
|
10
10
|
*
|
|
11
11
|
* This is the Kubernetes CRD/API-server split in Nomos terms:
|
|
12
|
-
* *
|
|
13
|
-
* * `DomainInstallation` is the workspace resource with spec/status/phase;
|
|
14
|
-
* * `installDomain` writes
|
|
15
|
-
* capability. `stageDomain`
|
|
12
|
+
* * the kernel-custodied USDA closure is the content-addressed law object;
|
|
13
|
+
* * `DomainInstallation` is the small workspace resource with spec/status/phase;
|
|
14
|
+
* * `installDomain` writes that reference in one intent and declares the `installDomain`
|
|
15
|
+
* capability. `stageDomain` records the same closure as Pending without serving it;
|
|
16
16
|
* `promoteDomain` and `rollbackDomain` are phase changes whose admission is judged
|
|
17
17
|
* against the staged package. This is the richer controller API; bootstrap exposes
|
|
18
18
|
* a thinner install-only intent for stage-zero.
|
|
@@ -43,16 +43,12 @@ export const DOMAIN_INSTALLATION_PHASES = [
|
|
|
43
43
|
] as const;
|
|
44
44
|
export type DomainInstallationPhase = (typeof DOMAIN_INSTALLATION_PHASES)[number];
|
|
45
45
|
|
|
46
|
-
export const PolicyBundle = aggregate("PolicyBundle", {
|
|
47
|
-
bundle: t.string().merge(Lww),
|
|
48
|
-
}).public();
|
|
49
|
-
|
|
50
46
|
export const DomainInstallation = aggregate("DomainInstallation", {
|
|
51
47
|
"spec.domainHash": t.string().merge(Lww),
|
|
52
48
|
"spec.packageHash": t.string().merge(Lww),
|
|
53
|
-
"spec.policyAggregate": t.string().merge(Lww),
|
|
54
49
|
"spec.authorityScope": t.string().merge(Lww),
|
|
55
50
|
"spec.installedBy": t.string().merge(Lww),
|
|
51
|
+
"spec.lawClosureIdentity": t.string().merge(Lww),
|
|
56
52
|
"spec.dependencies": t.set(t.string()),
|
|
57
53
|
"spec.supersedes": t.string().merge(Lww).optional(),
|
|
58
54
|
"spec.finalizers": t.set(t.string()),
|
|
@@ -95,18 +91,10 @@ export const ReleaseReceipt = aggregate(RELEASE_RECEIPT_AGGREGATE, {
|
|
|
95
91
|
acceptedAt: t.int().merge(Lww),
|
|
96
92
|
}).public();
|
|
97
93
|
|
|
98
|
-
export function policyAggregateId(domainHash: string): string {
|
|
99
|
-
return `policy:${domainHash}`;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
94
|
export function domainInstallationAggregateId(domainHash: string): string {
|
|
103
95
|
return `domain-installation:${domainHash}`;
|
|
104
96
|
}
|
|
105
97
|
|
|
106
|
-
function policyBundleInstance(domainHash: string) {
|
|
107
|
-
return instance(PolicyBundle, policyAggregateId(domainHash));
|
|
108
|
-
}
|
|
109
|
-
|
|
110
98
|
function domainInstallationInstance(domainHash: string) {
|
|
111
99
|
return instance(DomainInstallation, domainInstallationAggregateId(domainHash));
|
|
112
100
|
}
|
|
@@ -157,13 +145,15 @@ const preflightReceipt = z.object({
|
|
|
157
145
|
});
|
|
158
146
|
|
|
159
147
|
const packagePayload = {
|
|
160
|
-
domainHash:
|
|
161
|
-
packageUsda: z.string(),
|
|
148
|
+
domainHash: lowerHex64,
|
|
162
149
|
installedBy: z.string(),
|
|
150
|
+
lawClosureIdentity: lowerHex64,
|
|
163
151
|
authorityScope: z.string().default("workspace/root"),
|
|
164
|
-
|
|
152
|
+
// Omission means the empty set; keep that in the plan instead of encoding a composite default
|
|
153
|
+
// into canonical USDA merely to save `?? []` at runtime.
|
|
154
|
+
dependencies: z.array(z.string()).optional(),
|
|
165
155
|
supersedes: z.string().optional(),
|
|
166
|
-
finalizers: z.array(z.string()).
|
|
156
|
+
finalizers: z.array(z.string()).optional(),
|
|
167
157
|
};
|
|
168
158
|
|
|
169
159
|
export const installDomain = directive("installDomain")
|
|
@@ -185,15 +175,13 @@ export const installDomain = directive("installDomain")
|
|
|
185
175
|
}),
|
|
186
176
|
)
|
|
187
177
|
.plan((p): PlannedOp[] => {
|
|
188
|
-
const policy = policyBundleInstance(p.domainHash);
|
|
189
178
|
const install = domainInstallationInstance(p.domainHash);
|
|
190
179
|
const ops: PlannedOp[] = [
|
|
191
|
-
withMarker(set(
|
|
192
|
-
set(install, "spec.domainHash", p.domainHash),
|
|
180
|
+
withMarker(set(install, "spec.domainHash", p.domainHash), "ensures"),
|
|
193
181
|
set(install, "spec.packageHash", p.domainHash),
|
|
194
|
-
set(install, "spec.policyAggregate", policyAggregateId(p.domainHash)),
|
|
195
182
|
set(install, "spec.authorityScope", p.authorityScope),
|
|
196
183
|
set(install, "spec.installedBy", p.installedBy),
|
|
184
|
+
set(install, "spec.lawClosureIdentity", p.lawClosureIdentity),
|
|
197
185
|
set(install, "metadata.generation", 1),
|
|
198
186
|
set(install, "status.phase", "Active"),
|
|
199
187
|
set(install, "status.observedGeneration", 1),
|
|
@@ -231,15 +219,13 @@ export const stageDomain = directive("stageDomain")
|
|
|
231
219
|
{ message: "a staged domain package cannot supersede itself", path: ["supersedes"] },
|
|
232
220
|
))
|
|
233
221
|
.plan((p): PlannedOp[] => {
|
|
234
|
-
const policy = policyBundleInstance(p.domainHash);
|
|
235
222
|
const install = domainInstallationInstance(p.domainHash);
|
|
236
223
|
const ops: PlannedOp[] = [
|
|
237
|
-
withMarker(set(
|
|
238
|
-
set(install, "spec.domainHash", p.domainHash),
|
|
224
|
+
withMarker(set(install, "spec.domainHash", p.domainHash), "ensures"),
|
|
239
225
|
set(install, "spec.packageHash", p.domainHash),
|
|
240
|
-
set(install, "spec.policyAggregate", policyAggregateId(p.domainHash)),
|
|
241
226
|
set(install, "spec.authorityScope", p.authorityScope),
|
|
242
227
|
set(install, "spec.installedBy", p.installedBy),
|
|
228
|
+
set(install, "spec.lawClosureIdentity", p.lawClosureIdentity),
|
|
243
229
|
set(install, "metadata.generation", 1),
|
|
244
230
|
set(install, "status.phase", "Pending"),
|
|
245
231
|
set(install, "status.observedGeneration", 0),
|
|
@@ -146,7 +146,8 @@ export const advanceKeyEpoch = directive("advanceKeyEpoch")
|
|
|
146
146
|
wraps: z.array(wrapEntrySchema),
|
|
147
147
|
}),
|
|
148
148
|
)
|
|
149
|
-
.plan((p) => writeKeyEpoch(p.scope, p.epoch, p.openedAt, p.wraps))
|
|
149
|
+
.plan((p) => writeKeyEpoch(p.scope, p.epoch, p.openedAt, p.wraps))
|
|
150
|
+
.alsoEnsuresMany(WrappedKey);
|
|
150
151
|
|
|
151
152
|
/**
|
|
152
153
|
* `wrapKey` — write ONE wrap (the single-member handoff). Beyond its use as a primitive,
|
|
@@ -36,6 +36,7 @@ import { nomosSourceFrontierEnsure } from "../attested.js";
|
|
|
36
36
|
import * as birthMeter from "../birth_meter.js";
|
|
37
37
|
import * as birthMeterEnsure from "../birth_meter_ensure.js";
|
|
38
38
|
import * as birthMeterFramework from "./birth_meter.js";
|
|
39
|
+
import * as businessCorrections from "./business_corrections.js";
|
|
39
40
|
import * as authProviderLaw from "./auth_provider_law.js";
|
|
40
41
|
import * as disclosure from "./disclosure.js";
|
|
41
42
|
import * as domainLifecycle from "./domain_lifecycle.js";
|
|
@@ -59,6 +60,7 @@ const FRAMEWORK_NAMESPACES: ReadonlyArray<Record<string, unknown>> = [
|
|
|
59
60
|
birthMeter,
|
|
60
61
|
birthMeterEnsure,
|
|
61
62
|
birthMeterFramework,
|
|
63
|
+
businessCorrections,
|
|
62
64
|
authProviderLaw,
|
|
63
65
|
disclosure,
|
|
64
66
|
domainLifecycle,
|
package/src/framework/genesis.ts
CHANGED
|
@@ -9,18 +9,19 @@
|
|
|
9
9
|
*
|
|
10
10
|
* THE BOOTSTRAP PARADOX: the relation gate refuses a founding control-plane grant
|
|
11
11
|
* on a VIRGIN chain (an empty `#admin`/`#creator`/… roster) — there is no admin yet
|
|
12
|
-
* to gate the first admin. Resolved LAWFULLY (not by a host bypass):
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* to gate the first admin. Resolved LAWFULLY (not by a host bypass): the root
|
|
13
|
+
* `seedGenesis` is signed by the self-authenticating key principal it installs; a
|
|
14
|
+
* child receives its warrant only from the kernel's already-admitted parent-birth
|
|
15
|
+
* effect and the carried birth certificate rooted at K_root. The gate's GENESIS CLAUSE
|
|
15
16
|
* (`executor::genesis_admits`) then admits a founding grant on a virgin object IFF
|
|
16
17
|
* the warrant binds the chain to the captured principal. Strictly additive (only an
|
|
17
18
|
* otherwise-`Unauthorized` virgin grant), so every sealed chain replays unchanged.
|
|
18
19
|
*
|
|
19
|
-
* CONTROL-PLANE: `seedGenesis` is
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
20
|
+
* CONTROL-PLANE: `seedGenesis` is never a host assertion. At the root it carries an
|
|
21
|
+
* author block whose public key derives the claimed `key:<sha256>` principal and whose
|
|
22
|
+
* signature binds the exact workspace id. For descendants it is emitted only by the
|
|
23
|
+
* kernel after verifying the parent's admitted birth and K_root-rooted certificate.
|
|
24
|
+
* Every replaying peer re-verifies those cryptographic facts from custody.
|
|
24
25
|
*
|
|
25
26
|
* SINGLETON per chain: one founding principal per home/workspace, so the warrant is
|
|
26
27
|
* a singleton keyed `genesis-warrant:self`. `seedGenesis` is create-once — a second
|
|
@@ -32,7 +33,7 @@ import { t } from "../fields.js";
|
|
|
32
33
|
import { Lww } from "../drivers.js";
|
|
33
34
|
import { directive } from "../directive.js";
|
|
34
35
|
import { set, type PlannedOp } from "../ops.js";
|
|
35
|
-
import { writeTuple } from "./relations.js";
|
|
36
|
+
import { RelationTuple, writeTuple } from "./relations.js";
|
|
36
37
|
|
|
37
38
|
/**
|
|
38
39
|
* Deterministic singleton id of the founding warrant — MUST equal
|
|
@@ -42,16 +43,16 @@ export const GENESIS_WARRANT_ID = "genesis-warrant:self";
|
|
|
42
43
|
|
|
43
44
|
/**
|
|
44
45
|
* The founding-warrant record. It does THREE jobs at once:
|
|
45
|
-
* - `principal` — the
|
|
46
|
+
* - `principal` — the cryptographically proven founder (the genesis clause binds the founding grant to it);
|
|
46
47
|
* - `workspaceId` — this workspace's UNIQUE SELF-ID (the source binding the author signature covers,
|
|
47
48
|
* re-derived locally by the gate — NOT the shared/deterministic genesis commit, which is identical
|
|
48
49
|
* across all workspaces and so bound nothing);
|
|
49
50
|
* - its mere PRESENCE — the SIGNED-AUTHOR REGIME signal (the "era"): a workspace with a warrant
|
|
50
|
-
* REQUIRES a verified author block (forge closed)
|
|
51
|
-
*
|
|
51
|
+
* REQUIRES a verified author block (forge closed). A root seed proves itself against
|
|
52
|
+
* its own workspace-bound key; descendants additionally carry their parent-attested lineage.
|
|
52
53
|
*/
|
|
53
54
|
export const GenesisWarrant = aggregate("GenesisWarrant", {
|
|
54
|
-
principal: t.string().merge(Lww), // the
|
|
55
|
+
principal: t.string().merge(Lww), // the cryptographically proven founder, recorded at birth
|
|
55
56
|
workspaceId: t.string().merge(Lww), // this workspace's unique self-id (the author-signature source binding)
|
|
56
57
|
bornAt: t.string().merge(Lww), // ISO-8601, caller-stamped (determinism)
|
|
57
58
|
}).public();
|
|
@@ -104,4 +105,5 @@ export const seedGenesis = directive("seedGenesis")
|
|
|
104
105
|
grantedBy: p.principal,
|
|
105
106
|
grantedAt: p.bornAt,
|
|
106
107
|
}),
|
|
107
|
-
])
|
|
108
|
+
])
|
|
109
|
+
.alsoEnsures(RelationTuple);
|
package/src/framework/home.ts
CHANGED
|
@@ -37,9 +37,9 @@ import { query } from "../query.js";
|
|
|
37
37
|
import { birth } from "../births.js";
|
|
38
38
|
import { readFrameworkDeclared } from "../read.js";
|
|
39
39
|
import { authProvider as authProviderQuery, seedAuthProvider } from "./auth_provider_law.js";
|
|
40
|
-
import { workspaceInvariant, refAs } from "./workspace_invariant.js";
|
|
40
|
+
import { readRef, readSet, workspaceInvariant, refAs } from "./workspace_invariant.js";
|
|
41
41
|
import { identityRelations } from "./signing_identity.js";
|
|
42
|
-
import { writeTuple } from "./relations.js";
|
|
42
|
+
import { RelationTuple, writeTuple } from "./relations.js";
|
|
43
43
|
import { relationType, self, user, selfObject } from "../relation_schema.js";
|
|
44
44
|
import { requireParentAttestedBirth, type ParentAttestedBirthMarker } from "../births.js";
|
|
45
45
|
import { GenesisWarrant, seedGenesis } from "./genesis.js";
|
|
@@ -168,7 +168,8 @@ export function writeReceivingGroup(p: { principal: string; workspaceName: strin
|
|
|
168
168
|
export const seedReceivingGroup = directive("nomosInternalSeedReceivingGroup7c1f5e")
|
|
169
169
|
.ensures(ReceivingGroup)
|
|
170
170
|
.payload(z.object({ principal: z.string().min(1), workspaceName: z.string().min(1), bornAt: z.string().min(1) }))
|
|
171
|
-
.plan((p) => writeReceivingGroup(p))
|
|
171
|
+
.plan((p) => writeReceivingGroup(p))
|
|
172
|
+
.alsoEnsures(RelationTuple);
|
|
172
173
|
|
|
173
174
|
/**
|
|
174
175
|
* THE SQUAT INVARIANT (home_holons §5) — a home holon's self-committed `homeName` MUST be the hash of its
|
|
@@ -180,7 +181,7 @@ export const seedReceivingGroup = directive("nomosInternalSeedReceivingGroup7c1f
|
|
|
180
181
|
*/
|
|
181
182
|
export const receivingGroupNameBindsPrincipal = workspaceInvariant("ReceivingGroupNameBindsPrincipal")
|
|
182
183
|
.on("nomosInternalSeedReceivingGroup7c1f5e")
|
|
183
|
-
.reads((
|
|
184
|
+
.reads(readSet(readRef("group", ReceivingGroup).at(RECEIVING_GROUP_ID)))
|
|
184
185
|
.declassify("the receiving-group address is a public hash of its stable principal; refusing a mismatch reveals no private field")
|
|
185
186
|
.assert(({ group }) => {
|
|
186
187
|
const g = (group ?? {}) as { principal?: string; workspaceName?: string };
|
|
@@ -191,10 +192,10 @@ export const receivingGroupNameBindsPrincipal = workspaceInvariant("ReceivingGro
|
|
|
191
192
|
: { reject: "a receiving group's workspace name must hash its stable principal" };
|
|
192
193
|
});
|
|
193
194
|
|
|
194
|
-
const receivingGroupPolicyRefs = (
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
195
|
+
const receivingGroupPolicyRefs = readSet(
|
|
196
|
+
readRef("group", ReceivingGroup).at(RECEIVING_GROUP_ID),
|
|
197
|
+
readRef("policy", ReceivingGroupPolicy).at(RECEIVING_GROUP_POLICY_ID),
|
|
198
|
+
);
|
|
198
199
|
|
|
199
200
|
/** An IdP token may bootstrap the parent before the group exists; it may never bypass an existing group's
|
|
200
201
|
* device policy. A new group device must be approved by a current device or, later, guardian evidence. */
|
|
@@ -259,7 +260,7 @@ export function receivingGroupNameOf(principal: string): string {
|
|
|
259
260
|
* (no anonymous home birth); the offer principal IS the auth boundary (the tenant's setAuthProvider).
|
|
260
261
|
*
|
|
261
262
|
* The genesis chain it authors (reusing the order-declares-the-law install shape — bytes resolved by hash
|
|
262
|
-
* from the parent's
|
|
263
|
+
* from the parent's typed USDA closure custody):
|
|
263
264
|
* [ bootstrap/installDomain(frameworkHash), nomos/home installDomain(lawHash), seedHomeOwner(owner, …) ]
|
|
264
265
|
* `frameworkHash` (the bootstrap controller hash) + `lawHash` (the HOME law package domainHash) are
|
|
265
266
|
* tenant-PINNED at their governance compile (mirroring `birthChild`) — the end-user app supplies neither;
|
|
@@ -352,11 +353,11 @@ export function birthReceivingGroupForDomain(groupLawDomain: string) {
|
|
|
352
353
|
let certVerified = false;
|
|
353
354
|
try { certVerified = p.homeCert !== undefined && JSON.parse(p.homeCert).verified === true; } catch { /* malformed cert ⇒ unverified (the gate will refuse the birth anyway) */ }
|
|
354
355
|
const verifiedVia = certVerified ? apStr("issuer") : undefined;
|
|
355
|
-
const genesisChain: Array<{ domain: string; directiveId: string; actor: string; payload: Record<string, unknown>; domainHash?: string
|
|
356
|
-
// THE ORDER-DECLARED LAW INSTALL: two
|
|
357
|
-
// from the parent's custody when it folds the birth;
|
|
358
|
-
{ domain: "bootstrap", directiveId: "installDomain", actor: founder, payload: { domainHash: p.frameworkHash }, domainHash: p.frameworkHash
|
|
359
|
-
{ domain: "nomos", directiveId: "installDomain", actor: founder, payload: { domainHash: p.lawHash }, domainHash: p.lawHash
|
|
356
|
+
const genesisChain: Array<{ domain: string; directiveId: string; actor: string; payload: Record<string, unknown>; domainHash?: string }> = [
|
|
357
|
+
// THE ORDER-DECLARED LAW INSTALL: two typed closure identities. The kernel resolves each
|
|
358
|
+
// closure from the parent's custody when it folds the birth; law bytes never ride the intent.
|
|
359
|
+
{ domain: "bootstrap", directiveId: "installDomain", actor: founder, payload: { domainHash: p.frameworkHash }, domainHash: p.frameworkHash },
|
|
360
|
+
{ domain: "nomos", directiveId: "installDomain", actor: founder, payload: { domainHash: p.lawHash }, domainHash: p.lawHash },
|
|
360
361
|
{
|
|
361
362
|
domain: groupLawDomain,
|
|
362
363
|
directiveId: "nomosInternalSeedReceivingGroupPolicy7c1f5e",
|
|
@@ -509,6 +510,11 @@ export const pruneShare = directive("pruneShare")
|
|
|
509
510
|
/** "Show me everything <asserter> shared with me" — an indexed probe over the local access list. */
|
|
510
511
|
export const sharedByWorkspace = query("sharedByWorkspace").key("asserterWorkspace").returns(SharedWithMe);
|
|
511
512
|
|
|
513
|
+
/** "Show me every share for this protected object" — the indexed access-list probe used by generated
|
|
514
|
+
* application discovery. A home commonly has a very large installed law, so discovery must never fall back
|
|
515
|
+
* to the generic `aggregatesByType(SharedWithMe)` scan merely to find whole-workspace shares. */
|
|
516
|
+
export const sharedByObject = query("sharedByObject").key("object").returns(SharedWithMe);
|
|
517
|
+
|
|
512
518
|
// ─────────────────── THE PEER-SOVEREIGN ROOT-OF-TRUST (the BirthAttestation cert chain) ───────────────────
|
|
513
519
|
|
|
514
520
|
/**
|
|
@@ -288,7 +288,8 @@ function ownedRecordActionDirectives(aggregate: AggregateHandle) {
|
|
|
288
288
|
grantedAt: p.sharedAt,
|
|
289
289
|
}),
|
|
290
290
|
...p.wraps.flatMap((entry) => writeWrap(recordScope(aggregate, p.recordId), entry.epoch, entry)),
|
|
291
|
-
])
|
|
291
|
+
])
|
|
292
|
+
.alsoEnsures(RelationTuple),
|
|
292
293
|
{ businessBirthInternal: true as const },
|
|
293
294
|
);
|
|
294
295
|
|
|
@@ -313,7 +314,9 @@ function ownedRecordActionDirectives(aggregate: AggregateHandle) {
|
|
|
313
314
|
revokedAt: p.changedAt,
|
|
314
315
|
}),
|
|
315
316
|
...writeKeyEpoch(recordScope(aggregate, p.recordId), p.epoch, p.changedAt, p.wraps),
|
|
316
|
-
])
|
|
317
|
+
])
|
|
318
|
+
.alsoEnsures(RelationTuple)
|
|
319
|
+
.alsoEnsuresMany(WrappedKey),
|
|
317
320
|
{ businessBirthInternal: true as const },
|
|
318
321
|
);
|
|
319
322
|
|
|
@@ -354,7 +357,10 @@ function ownedRecordActionDirectives(aggregate: AggregateHandle) {
|
|
|
354
357
|
ops.push(...writeKeyEpoch(scope, p.epoch, p.changedAt, []));
|
|
355
358
|
for (const entry of p.historicWraps) ops.push(...writeWrap(scope, entry.epoch, entry));
|
|
356
359
|
return ops;
|
|
357
|
-
})
|
|
360
|
+
})
|
|
361
|
+
.alsoEnsuresMany(RelationTuple)
|
|
362
|
+
.alsoMutates(aggregate)
|
|
363
|
+
.alsoEnsuresMany(WrappedKey),
|
|
358
364
|
{ businessBirthInternal: true as const },
|
|
359
365
|
);
|
|
360
366
|
|
|
@@ -102,7 +102,7 @@ import { directive, type RequirableDirective } from "../directive.js";
|
|
|
102
102
|
import { set } from "../ops.js";
|
|
103
103
|
import { query, type QueryDecl } from "../query.js";
|
|
104
104
|
import { writeTuple, RelationTuple } from "./relations.js";
|
|
105
|
-
import { workspaceInvariant, refAs, type WorkspaceInvariantDecl } from "./workspace_invariant.js";
|
|
105
|
+
import { readRef, readSet, workspaceInvariant, refAs, type WorkspaceInvariantDecl } from "./workspace_invariant.js";
|
|
106
106
|
|
|
107
107
|
/** The founder singleton's stable instance id — one founder per platform workspace (`workspace:self`). */
|
|
108
108
|
export const PLATFORM_FOUNDER_ID = "platform-founder:self";
|
|
@@ -215,7 +215,7 @@ export function seedPlatformAdmin(opts: SeedPlatformAdminOptions): PlatformAdmin
|
|
|
215
215
|
*/
|
|
216
216
|
const platformFounderWriteOnce = workspaceInvariant(writeOnceInvariantId)
|
|
217
217
|
.on(founderDirectiveId)
|
|
218
|
-
.reads((
|
|
218
|
+
.reads(readSet(readRef("prior", founderAggregateId).pre().at(PLATFORM_FOUNDER_ID)))
|
|
219
219
|
.assert(({ prior }, ctx) => {
|
|
220
220
|
const seated = (prior ?? {}) as { principal?: string };
|
|
221
221
|
// WRITE-ONCE: a founder already recorded ⇒ refuse a re-seat (the seat is immutable after genesis).
|
|
@@ -260,7 +260,7 @@ export function seedPlatformAdmin(opts: SeedPlatformAdminOptions): PlatformAdmin
|
|
|
260
260
|
*/
|
|
261
261
|
const platformAdminBindsFounder = workspaceInvariant(bindsInvariantId)
|
|
262
262
|
.on(seatDirectiveId)
|
|
263
|
-
.reads((
|
|
263
|
+
.reads(readSet(readRef("founder", founderAggregateId).at(PLATFORM_FOUNDER_ID)))
|
|
264
264
|
.assert(({ founder }, ctx) => {
|
|
265
265
|
const rec = (founder ?? {}) as { principal?: string };
|
|
266
266
|
const intent = (ctx?.intent ?? {}) as { founder?: string };
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* ── The aggregate (object-keyed) ─────────────────────────────────────────────
|
|
16
16
|
* `RelationTuple` is keyed `reltuple-<object>-<relation>` — ONE aggregate per
|
|
17
17
|
* (object, relation) pair — carrying a `subjects: MapOf(Lww)` map of `subject → leaf`,
|
|
18
|
-
* where each leaf is a
|
|
18
|
+
* where each leaf is a typed `RelationshipSubject` (`active` | `revoked`). The projection folds an
|
|
19
19
|
* ACTIVE subject into a `relations(object, relation, subject)` row; a revoked subject
|
|
20
20
|
* yields no row (Check fails closed).
|
|
21
21
|
*
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
*/
|
|
42
42
|
import { z } from "zod";
|
|
43
43
|
import { aggregate, instance } from "../aggregate.js";
|
|
44
|
-
import { t } from "../fields.js";
|
|
44
|
+
import { t, type JsonValue } from "../fields.js";
|
|
45
45
|
import { Lww, MapOf } from "../drivers.js";
|
|
46
46
|
import { directive } from "../directive.js";
|
|
47
47
|
import { set, setEntry, withMarker, type PlannedOp } from "../ops.js";
|
|
@@ -70,6 +70,11 @@ import { qualifyReadProjectionType } from "../read_projection_namespace.js";
|
|
|
70
70
|
interface DispatchContext {
|
|
71
71
|
/** The dispatching domain key — the qualifier (`module.domain === intent.domain`). */
|
|
72
72
|
readonly domain: string;
|
|
73
|
+
/**
|
|
74
|
+
* The durable Zanzibar namespace for this domain. Canonical USDA uses the domain's stable identity
|
|
75
|
+
* (`d_<sid>`), not its mutable dispatch label. Older/test engines may omit this and retain the label.
|
|
76
|
+
*/
|
|
77
|
+
readonly relationNamespace?: string;
|
|
73
78
|
/** `<type>#<relation>` for every LOCAL non-global authored relation of this domain — the qualify set. */
|
|
74
79
|
readonly qualifiable: ReadonlySet<string>;
|
|
75
80
|
/**
|
|
@@ -122,14 +127,14 @@ function resolveTupleObject(object: string | RelationObjectToken, relation: stri
|
|
|
122
127
|
"qualify by). Tokens (selfWorkspace()/selfObject()) resolve only inside a directive plan.",
|
|
123
128
|
);
|
|
124
129
|
}
|
|
125
|
-
return `${qualifyRelationType(_ctx.domain, object.type)}:${object.id}`;
|
|
130
|
+
return `${qualifyRelationType(_ctx.relationNamespace ?? _ctx.domain, object.type)}:${object.id}`;
|
|
126
131
|
}
|
|
127
132
|
// A PLAIN STRING is classified by `(type, relation)` against the dispatching domain's schema.
|
|
128
133
|
const colon = object.indexOf(":");
|
|
129
134
|
if (colon < 0 || _ctx === undefined) return object; // malformed / no context ⇒ verbatim
|
|
130
135
|
const type = object.slice(0, colon);
|
|
131
136
|
if (_ctx.qualifiable.has(`${type}#${relation}`)) {
|
|
132
|
-
return `${qualifyRelationType(_ctx.domain, type)}${object.slice(colon)}`;
|
|
137
|
+
return `${qualifyRelationType(_ctx.relationNamespace ?? _ctx.domain, type)}${object.slice(colon)}`;
|
|
133
138
|
}
|
|
134
139
|
return object; // global / unknown / per-record ⇒ BARE
|
|
135
140
|
}
|
|
@@ -185,7 +190,7 @@ export function relationTupleInstanceId(object: string, relation: string): strin
|
|
|
185
190
|
*
|
|
186
191
|
* - object Lww (the address, materialised on first relate so the aggregate exists)
|
|
187
192
|
* - relation Lww (the edge label)
|
|
188
|
-
* - subjects MapOf(Lww) — subject →
|
|
193
|
+
* - subjects MapOf(Lww) — subject → typed `RelationshipSubject` runtime leaf
|
|
189
194
|
*/
|
|
190
195
|
export const RelationTuple = aggregate("RelationTuple", {
|
|
191
196
|
object: t.string().merge(Lww),
|
|
@@ -194,7 +199,7 @@ export const RelationTuple = aggregate("RelationTuple", {
|
|
|
194
199
|
}).public();
|
|
195
200
|
export type RelationTupleHandle = typeof RelationTuple;
|
|
196
201
|
|
|
197
|
-
/** The per-subject leaf the `subjects` map stores
|
|
202
|
+
/** The per-subject leaf the `subjects` map stores as a typed kernel value. */
|
|
198
203
|
export interface RelationSubjectLeaf {
|
|
199
204
|
status: RelationStatus;
|
|
200
205
|
grantedBy: string;
|
|
@@ -222,11 +227,11 @@ export interface RelationSubjectLeaf {
|
|
|
222
227
|
ownedCustody?: string;
|
|
223
228
|
}
|
|
224
229
|
|
|
225
|
-
/** Strip undefined keys so the
|
|
226
|
-
function compact(obj: Record<string, unknown>): RelationSubjectLeaf {
|
|
230
|
+
/** Strip undefined keys so the typed leaf has one stable optional-field shape. */
|
|
231
|
+
function compact(obj: Record<string, unknown>): RelationSubjectLeaf & Record<string, JsonValue> {
|
|
227
232
|
const out: Record<string, unknown> = {};
|
|
228
233
|
for (const [k, v] of Object.entries(obj)) if (v !== undefined) out[k] = v;
|
|
229
|
-
return out as
|
|
234
|
+
return out as RelationSubjectLeaf & Record<string, JsonValue>;
|
|
230
235
|
}
|
|
231
236
|
|
|
232
237
|
interface WriteTupleInput {
|
|
@@ -277,9 +282,9 @@ function lowerTuple(
|
|
|
277
282
|
// subject's endorsement is recorded on the setEntry op so the discovery harvest lifts it into
|
|
278
283
|
// nomosFlowGate.endorse; grantedBy is free attribution (materialised, not gated).
|
|
279
284
|
//
|
|
280
|
-
// The captured authoring principal IS already a wire-form subject (`user:<uid>` /
|
|
281
|
-
//
|
|
282
|
-
//
|
|
285
|
+
// The captured authoring principal IS already a kernel-proven wire-form subject (`user:<uid>` /
|
|
286
|
+
// `key:<hash>` — the SAME form the write gate parses via `Subject::parse`), so an endorsed authority
|
|
287
|
+
// subject lowers to that leaf verbatim and the gate's `engine.check` matches it.
|
|
283
288
|
const subjectE = endorsementOf(p.subject);
|
|
284
289
|
const subjectRaw = subjectE
|
|
285
290
|
? subjectE.principal
|
|
@@ -300,7 +305,10 @@ function lowerTuple(
|
|
|
300
305
|
verifiedVia: p.verifiedVia,
|
|
301
306
|
ownedCustody: p.ownedCustody,
|
|
302
307
|
});
|
|
303
|
-
const subjectEntry =
|
|
308
|
+
const subjectEntry = {
|
|
309
|
+
...setEntry(rt, "subjects", subjectRaw, leaf),
|
|
310
|
+
kernelValue: { RelationshipSubject: leaf },
|
|
311
|
+
};
|
|
304
312
|
return [
|
|
305
313
|
// upsert the aggregate head (first relate creates it); without this the referential
|
|
306
314
|
// gate would reject a setEntry on an absent id.
|