@githolon/dsl 0.108.6 → 0.110.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/assets/operation-contract.wasm +0 -0
- package/check_package.mjs +12 -15
- package/compile_package.mjs +17 -18
- package/compile_proof.mjs +16 -1
- package/dart/CHANGELOG.md +76 -1
- package/dart/README.md +25 -2
- package/dart/lib/src/admission_decision.dart +41 -0
- package/dart/lib/src/application_framework_calls.dart +52 -2
- package/dart/lib/src/application_model_adoption.dart +187 -0
- package/dart/lib/src/application_status.dart +73 -0
- package/dart/lib/src/console_enrolment.dart +271 -0
- package/dart/lib/src/console_http_io.dart +26 -0
- package/dart/lib/src/console_http_stub.dart +7 -0
- package/dart/lib/src/dev_cloud.dart +69 -22
- package/dart/lib/src/generated_call.dart +83 -23
- package/dart/lib/src/kernel_primitives.dart +119 -0
- package/dart/lib/src/nms_conformance_runtime.dart +1 -1
- package/dart/lib/src/nms_generated_runtime.dart +32 -10
- package/dart/lib/src/nms_test_environment.dart +1 -1
- package/dart/lib/src/node_runner_transport.dart +41 -46
- package/dart/lib/src/observed_session_directory.dart +70 -0
- package/dart/lib/src/operation_receipts.dart +112 -0
- package/dart/lib/src/peer_application_streams.dart +103 -0
- package/dart/lib/src/reception/dart_reception.dart +4280 -0
- package/dart/lib/src/reception/event_desk.dart +211 -0
- package/dart/lib/src/reception/extremum_read.dart +46 -0
- package/dart/lib/src/reception/local_copies.dart +371 -0
- package/dart/lib/src/reception/peer_office_connection.dart +11 -0
- package/dart/lib/src/reception/progress_desk.dart +267 -0
- package/dart/lib/src/reception/reception_contract.dart +11 -0
- package/dart/lib/src/reception/reception_session.dart +284 -0
- package/dart/lib/src/reception/sync_diagnostics.dart +26 -0
- package/dart/lib/src/reception/workspace_preparation_report.dart +126 -0
- package/dart/lib/src/replica_readiness.dart +123 -0
- package/dart/lib/src/runner/node-entry.mjs +23905 -15774
- package/dart/lib/src/runtime_v2_gateway.dart +56 -3
- package/dart/lib/src/runtime_v2_transport.dart +544 -3
- package/dart/lib/src/session_bound.dart +200 -311
- package/dart/lib/src/signing_identity_kernel.dart +84 -0
- package/dart/lib/src/sync_status_projection.dart +548 -0
- package/dart/lib/src/sync_status_reducer.dart +170 -0
- package/dart/lib/src/test_harness.dart +809 -1153
- package/dart/lib/src/testing/test_custody.dart +66 -0
- package/dart/lib/src/testing/test_peer.dart +328 -0
- package/dart/lib/src/typed_watch_registry.dart +169 -0
- package/dart/lib/src/workspace_open_progress.dart +27 -0
- package/dart/pubspec.lock +24 -1
- package/dart/pubspec.yaml +7 -2
- package/dart/pubspec_overrides.yaml +2 -0
- package/dart/test/admission_decision_test.dart +48 -0
- package/dart/test/background_model_adoption_test.dart +228 -0
- package/dart/test/bundle_version_pairing_test.dart +4 -4
- package/dart/test/child_authentication_repair_test.dart +359 -0
- package/dart/test/cloud_bound_local_shell_test.dart +119 -0
- package/dart/test/console_enrolment_refusal_test.dart +52 -0
- package/dart/test/console_enrolment_test.dart +227 -0
- package/dart/test/errors_test.dart +5 -5
- package/dart/test/event_plane_seam_test.dart +50 -164
- package/dart/test/extremum_read_test.dart +58 -0
- package/dart/test/fixtures/wedge/wedge-entry.mjs +3 -21
- package/dart/test/generated_query_test.dart +32 -0
- package/dart/test/harness_phase_timeout_test.dart +18 -23
- package/dart/test/kernel_author_after_read_test.dart +242 -0
- package/dart/test/kernel_fence_test.dart +137 -0
- package/dart/test/local_copies_test.dart +222 -0
- package/dart/test/model_adoption_test.dart +198 -0
- package/dart/test/node_peer_local_custody_test.dart +153 -0
- package/dart/test/offer_authentication_proof_test.dart +352 -52
- package/dart/test/operation_compatibility_test.dart +151 -0
- package/dart/test/platform_credential_effect_test.dart +386 -0
- package/dart/test/receiving_group_open_test.dart +213 -0
- package/dart/test/reception/workspace_preparation_report_test.dart +103 -0
- package/dart/test/reception_span_trace_test.dart +23 -0
- package/dart/test/replica_readiness_test.dart +156 -0
- package/dart/test/runtime_projections_test.dart +92 -0
- package/dart/test/runtime_v2_gateway_test.dart +131 -0
- package/dart/test/signing_identity_kernel_test.dart +75 -0
- package/dart/test/sync_status_preparation_report_test.dart +29 -0
- package/dart/test/sync_status_reducer_test.dart +419 -0
- package/dart/test/test_harness_test.dart +71 -95
- package/dart/test/typed_query_watch_test.dart +170 -0
- package/dart/test/typed_watch_registry_test.dart +249 -0
- package/dart_lints/README.md +18 -0
- package/dart_lints/lib/nomos_lints.dart +70 -0
- package/dart_lints/pubspec.lock +580 -0
- package/dart_lints/pubspec.yaml +12 -0
- package/dart_lints/test/reference_identity_test.dart +75 -0
- package/dart_types/CHANGELOG.md +18 -0
- package/dart_types/lib/nomos_kernel_protocol.dart +21 -1
- package/dart_types/lib/nomos_types.dart +9 -1
- package/dart_types/lib/src/admission_decision.dart +37 -0
- package/dart_types/lib/src/aggregate_meta.dart +1 -1
- package/dart_types/lib/src/approved_model.dart +65 -0
- package/dart_types/lib/src/custody.dart +10 -18
- package/dart_types/lib/src/error_info.dart +4 -4
- package/dart_types/lib/src/errors.dart +29 -29
- package/dart_types/lib/src/foreign_ref.dart +62 -33
- package/dart_types/lib/src/local_work_rejected.dart +3 -3
- package/dart_types/lib/src/nomos_ref.dart +3 -1
- package/dart_types/lib/src/principal.dart +2 -2
- package/dart_types/lib/src/proto/google/protobuf/any.pb.dart +163 -0
- package/dart_types/lib/src/proto/google/protobuf/any.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pb.dart +137 -0
- package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +20721 -10433
- package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +555 -27
- package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pb.dart +387 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pbenum.dart +46 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pb.dart +295 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pbenum.dart +139 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pb.dart +269 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pbenum.dart +103 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pb.dart +417 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pbenum.dart +62 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pb.dart +9968 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pbenum.dart +133 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pb.dart +204 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pbenum.dart +54 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pb.dart +1550 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pb.dart +710 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pb.dart +845 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pb.dart +198 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pb.dart +369 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pb.dart +609 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pbenum.dart +65 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pb.dart +358 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pbenum.dart +100 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pb.dart +2231 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pbenum.dart +271 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pb.dart +1286 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pb.dart +636 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pbenum.dart +116 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pb.dart +1637 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pbenum.dart +175 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pb.dart +1269 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pbenum.dart +129 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pb.dart +2782 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pbenum.dart +97 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pb.dart +2232 -8627
- package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pbenum.dart +0 -358
- package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pb.dart +1639 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pbenum.dart +46 -0
- package/dart_types/lib/src/runtime_outcome.dart +133 -0
- package/dart_types/lib/src/subscriptions.dart +116 -23
- package/dart_types/pubspec.yaml +1 -1
- package/dart_types/test/extremum_subscriptions_test.dart +40 -0
- package/dart_types/test/foreign_ref_resolution_test.dart +75 -0
- package/dart_types/test/nomos_ref_address_test.dart +25 -0
- package/dart_types/test/ordered_subscriptions_test.dart +34 -0
- package/operation_contract_check.mjs +24 -0
- package/package.json +11 -6
- package/src/aggregate.ts +70 -10
- package/src/application_topology.ts +18 -11
- package/src/attested.ts +3 -0
- package/src/attested_core.ts +46 -3
- package/src/birth_meter_ensure.ts +19 -7
- package/src/births.ts +29 -4
- package/src/build_package.ts +140 -13
- package/src/business_birth.ts +181 -4
- package/src/codegen_call_proto.ts +1274 -0
- package/src/codegen_call_usda.ts +49 -6
- package/src/codegen_dart.ts +79 -44
- package/src/codegen_dart_client.ts +458 -800
- package/src/{codegen_dart_application.ts → codegen_dart_facade.ts} +331 -582
- package/src/codegen_dart_framework_calls.ts +320 -37
- package/src/codegen_dart_indexed_variant.ts +159 -0
- package/src/codegen_dart_scenario.ts +42 -13
- package/src/codegen_proof.ts +3 -3
- package/src/codegen_provider_dart.ts +5 -0
- package/src/codegen_query_proto.ts +158 -0
- package/src/codegen_refusal.ts +189 -0
- package/src/codegen_result_proto.ts +931 -0
- package/src/codegen_result_usda.ts +4 -0
- package/src/codegen_ts.ts +464 -616
- package/src/codegen_workspace_access.ts +105 -0
- package/src/compatibility_graph.ts +759 -0
- package/src/compatibility_usda.ts +264 -0
- package/src/compile_package_main.ts +2083 -1248
- package/src/custody_placement.ts +156 -0
- package/src/custody_placement_usda.ts +91 -0
- package/src/decl_shapes.ts +21 -2
- package/src/describe_model.ts +1 -0
- package/src/directive.ts +59 -2
- package/src/drivers.ts +51 -0
- package/src/engine_entry.ts +52 -8
- package/src/evolve_preview.ts +11 -4
- package/src/exists.ts +2 -0
- package/src/fields.ts +75 -11
- package/src/flow_gate.ts +2 -1
- package/src/framework/application_home.ts +170 -18
- package/src/framework/attested_receipts.ts +8 -3
- package/src/framework/auth_provider_law.ts +169 -9
- package/src/framework/birth_certificate_law.ts +4 -0
- package/src/framework/capability.ts +34 -3
- package/src/framework/child_authentication.ts +346 -0
- package/src/framework/console_governance.ts +295 -0
- package/src/framework/delegated_roles.ts +301 -0
- package/src/framework/domain_lifecycle.ts +26 -13
- package/src/framework/founding_genesis.ts +193 -0
- package/src/framework/framework_aggregates.ts +1 -1
- package/src/framework/genesis.ts +6 -1
- package/src/framework/governance_relations.ts +19 -0
- package/src/framework/home.ts +5 -734
- package/src/framework/legacy/child_auth_provider_epoch.ts +73 -0
- package/src/framework/model_authority_repair.ts +116 -0
- package/src/framework/model_authority_seed.ts +185 -0
- package/src/framework/model_transition.ts +211 -0
- package/src/framework/owned_custody.ts +1 -1
- package/src/framework/principal_enrollment.ts +302 -0
- package/src/framework/receiving_group.ts +857 -0
- package/src/framework/receiving_group_recovery.ts +314 -0
- package/src/framework/relations.ts +4 -8
- package/src/framework/signing_identity.ts +242 -95
- package/src/framework/source_roster.ts +11 -5
- package/src/framework/workspace_access.ts +149 -0
- package/src/framework/workspace_invariant.ts +73 -52
- package/src/framework/workspaces.ts +407 -40
- package/src/framework_call_definitions.ts +32 -3
- package/src/generated_client_bundle_boundary.ts +97 -0
- package/src/generated_client_names.ts +1 -0
- package/src/identity_usda.ts +27 -0
- package/src/index.ts +183 -12
- package/src/indexed_variant.ts +83 -0
- package/src/intent.ts +173 -45
- package/src/law_check.ts +8 -5
- package/src/law_dependency_index.ts +231 -0
- package/src/law_graph.ts +508 -71
- package/src/law_graph_usda.ts +170 -24
- package/src/law_identity.ts +119 -81
- package/src/law_source_map.ts +4 -1
- package/src/law_units.ts +498 -0
- package/src/law_value_type.ts +7 -3
- package/src/member_authentication.ts +302 -0
- package/src/null_meta.ts +1 -1
- package/src/openusd_compiled_layer.ts +132 -0
- package/src/operation_contract_check.ts +31 -0
- package/src/ordered_read.ts +2 -1
- package/src/package_config.ts +33 -12
- package/src/package_dependencies.ts +1 -0
- package/src/plan_source_slice.ts +30 -1
- package/src/protobuf_field_number.ts +36 -0
- package/src/provenance_wire.ts +6 -0
- package/src/query.ts +2 -1
- package/src/read.ts +50 -0
- package/src/relation.ts +4 -1
- package/src/relation_schema.ts +15 -2
- package/src/report.ts +2 -2
- package/src/sqlite_computation_bundle.ts +78 -0
- package/src/sqlite_computed.ts +102 -0
- package/src/sqlite_counts.ts +154 -0
- package/src/sqlite_layout.ts +823 -0
- package/src/sqlite_reads.ts +275 -0
- package/src/sqlite_samples.ts +185 -0
- package/src/stable_id_identity_bridge.ts +1 -1
- package/src/stable_ids_for_module.ts +16 -4
- package/src/test_call_usda.ts +6 -6
- package/src/usda_relationship_integrity.ts +20 -3
- package/src/usda_text.ts +21 -1
- package/src/value_object.ts +81 -11
- package/src/wire.ts +22 -0
- package/src/wire_encode.ts +227 -20
- package/src/workspace_sharding.ts +15 -6
- package/src/workspace_sharing.ts +51 -49
- package/src/zod_introspection.ts +2 -0
- package/compat_package.mjs +0 -46
- package/dart/lib/src/home.dart +0 -15
- package/dart/lib/src/runner/offline-install.mjs +0 -15367
- package/dart/lib/src/source_trust.dart +0 -51
- package/dart/lib/src/webview_bridge.dart +0 -6540
- package/dart/lib/src/workspace_name.dart +0 -30
- package/dart/test/acquire_session_credentials_test.dart +0 -150
- package/dart/test/acquire_session_test.dart +0 -534
- package/dart/test/adv_realm_interference_test.dart +0 -600
- package/dart/test/cloud_bound_topology_test.dart +0 -168
- package/dart/test/cluster_births_test.dart +0 -202
- package/dart/test/cluster_device_keyed_acceptance_test.dart +0 -105
- package/dart/test/cluster_local_autosync_test.dart +0 -123
- package/dart/test/cluster_scale_test.dart +0 -122
- package/dart/test/compat_test.dart +0 -61
- package/dart/test/dlq_session_surface_test.dart +0 -222
- package/dart/test/first_app_gauntlet_test.dart +0 -386
- package/dart/test/fixtures/wedge/fake-install.mjs +0 -11
- package/dart/test/four_layer_nested_topology_test.dart +0 -290
- package/dart/test/home_parity_test.dart +0 -13
- package/dart/test/local_sync_inert_test.dart +0 -107
- package/dart/test/node_runner_transport_test.dart +0 -78
- package/dart/test/parent_signer_recovery_test.dart +0 -155
- package/dart/test/residency_refusal_test.dart +0 -49
- package/dart/test/sealing_client_test.dart +0 -181
- package/dart/test/session_bound_test.dart +0 -285
- package/dart/test/stale_bundle_test.dart +0 -80
- package/dart/test/vo_read_typing_test.dart +0 -131
- package/dart/test/watch_after_offer_stall_test.dart +0 -100
- package/dart/test/webview_bridge_test.dart +0 -1930
- package/dart/test/workspace_name_test.dart +0 -24
- package/runtime/generated-application-host.js +0 -22860
- package/src/codegen_query_usda.ts +0 -214
- package/src/compat_manifest.ts +0 -26
- package/src/compat_package_main.ts +0 -224
- package/src/shape_compat.ts +0 -352
- package/src/usd_state.ts +0 -505
package/src/value_object.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* THE TENANT GAP THIS CLOSES: a structured value on an aggregate was a hand-rolled
|
|
13
13
|
* `t.jsonObject()` plus a privately-kept zod schema per site — untyped in the generated
|
|
14
14
|
* clients, invisible in the package IR. A value object is the NAMED, REUSABLE,
|
|
15
|
-
* COMPOSABLE
|
|
15
|
+
* COMPOSABLE replacement for that opaque leaf:
|
|
16
16
|
*
|
|
17
17
|
* const PostalAddress = valueObject("PostalAddress", {
|
|
18
18
|
* streetAddress: z.string(), city: z.string(), countryCode: z.string(),
|
|
@@ -28,13 +28,12 @@
|
|
|
28
28
|
* ONE definition produces: the zod schema (the SAME zod the engine validates — embed
|
|
29
29
|
* `SiteLocation.zod` in a directive payload), the inferred TS type (`SiteLocation.field()`
|
|
30
30
|
* types `set()` and the generated `.client.ts` names the interface), the generated Dart
|
|
31
|
-
* class (typed decode from the projection's
|
|
31
|
+
* class (typed decode from the projection's nominal value), and a canonical
|
|
32
32
|
* descriptor that rides the package IR (the additive `nomosValueObjects` USD key) so
|
|
33
33
|
* tooling/visualizers can SEE the structure.
|
|
34
34
|
*
|
|
35
|
-
* STORAGE / MERGE SEMANTICS
|
|
36
|
-
*
|
|
37
|
-
* on write (exactly the encrypted-envelope rule). There is NO per-subfield merge: two
|
|
35
|
+
* STORAGE / MERGE SEMANTICS: a VO is encoded as named, recursively typed USDA prims. The field
|
|
36
|
+
* remains ONE Lww-class value, replaced WHOLESALE on write. There is NO per-subfield merge: two
|
|
38
37
|
* concurrent writes to different subfields of one VO converge to ONE writer's whole
|
|
39
38
|
* value. When subfields must merge independently, model them as aggregate fields or a
|
|
40
39
|
* `t.map(...)` — that is what aggregates/maps are for.
|
|
@@ -79,6 +78,8 @@ type ZodInternalDef = {
|
|
|
79
78
|
entries?: Record<string, string | number>;
|
|
80
79
|
valueType?: z.ZodTypeAny;
|
|
81
80
|
keyType?: z.ZodTypeAny;
|
|
81
|
+
shape?: Record<string, z.ZodTypeAny> | (() => Record<string, z.ZodTypeAny>);
|
|
82
|
+
options?: readonly z.ZodTypeAny[];
|
|
82
83
|
};
|
|
83
84
|
function zodDef(zt: z.ZodTypeAny): ZodInternalDef {
|
|
84
85
|
const raw = zt as unknown as { _def?: ZodInternalDef; def?: ZodInternalDef };
|
|
@@ -88,6 +89,10 @@ function zodKind(zt: z.ZodTypeAny): string {
|
|
|
88
89
|
const t = zodDef(zt).type;
|
|
89
90
|
return typeof t === "string" ? t : "unknown";
|
|
90
91
|
}
|
|
92
|
+
function zodObjectShape(zt: z.ZodTypeAny): Record<string, z.ZodTypeAny> {
|
|
93
|
+
const raw = zodDef(zt).shape;
|
|
94
|
+
return typeof raw === "function" ? raw() : raw ?? {};
|
|
95
|
+
}
|
|
91
96
|
function zodEnumValues(zt: z.ZodTypeAny): string[] {
|
|
92
97
|
const raw = zt as unknown as { options?: readonly unknown[] };
|
|
93
98
|
if (raw.options !== undefined) return raw.options.map(String);
|
|
@@ -142,8 +147,8 @@ export interface ValueObject<T = unknown> {
|
|
|
142
147
|
* for an optional aggregate FIELD use `.field().optional()`). */
|
|
143
148
|
optional(): OptionalValueObject<T>;
|
|
144
149
|
/**
|
|
145
|
-
* Lower this VO to an aggregate FIELD: one
|
|
146
|
-
*
|
|
150
|
+
* Lower this VO to an aggregate FIELD: one nominal structural value (Lww) whose whole value is
|
|
151
|
+
* this VO —
|
|
147
152
|
* WHOLESALE-REPLACE merge, no per-subfield merge (v1). Chain the usual field
|
|
148
153
|
* modifiers: `.merge(Lww/LwwCausal/LwwBy)` (Lww-class only — the value is replaced
|
|
149
154
|
* whole), `.optional()`, `.encrypted()` (under the existing `requires(cap)` rules).
|
|
@@ -152,7 +157,7 @@ export interface ValueObject<T = unknown> {
|
|
|
152
157
|
/**
|
|
153
158
|
* SUGAR for `.field().merge(driver)` — lower this VO DIRECTLY to an aggregate field with
|
|
154
159
|
* the chosen merge driver: `location: SiteLocation.merge(Lww).optional()`. A VO field is
|
|
155
|
-
* ONE Lww-class
|
|
160
|
+
* ONE Lww-class structural value replaced WHOLESALE, so only the Lww-class family
|
|
156
161
|
* (`Lww` / `LwwCausal` / `LwwBy`) is accepted — any other driver throws (field-level merge
|
|
157
162
|
* composition of a VO's subfields is a later slice; model those as aggregate fields / a
|
|
158
163
|
* `t.map(...)`). The returned `Field` chains the usual modifiers (`.optional()`,
|
|
@@ -596,6 +601,10 @@ function describeZod(
|
|
|
596
601
|
const inner = describeZod(voName, path, zodDef(schema).innerType!, deps);
|
|
597
602
|
return { ...inner, optional: true };
|
|
598
603
|
}
|
|
604
|
+
case "nullable": {
|
|
605
|
+
const inner = describeZod(voName, path, zodDef(schema).innerType!, deps);
|
|
606
|
+
return { ...inner, nullable: true };
|
|
607
|
+
}
|
|
599
608
|
default:
|
|
600
609
|
throw new Error(
|
|
601
610
|
`valueObject '${voName}': field '${path}' uses zod shape '${zodKind(schema)}', which is outside ` +
|
|
@@ -651,6 +660,7 @@ function sampleOfDescriptorField(
|
|
|
651
660
|
includeOptional = false,
|
|
652
661
|
path = fieldName,
|
|
653
662
|
): unknown {
|
|
663
|
+
if (f.nullable === true) return null;
|
|
654
664
|
switch (f.type) {
|
|
655
665
|
case "string": return `sample-${fieldName}`;
|
|
656
666
|
case "number": return 1;
|
|
@@ -769,7 +779,7 @@ function makeValueObject<T>(
|
|
|
769
779
|
},
|
|
770
780
|
merge(driver: Driver): Field<T, "json"> {
|
|
771
781
|
const k = driver.kind;
|
|
772
|
-
if (k !== "FirstWriterWins" && k !== "Lww" && k !== "LwwCausal" && k !== "LwwBy") {
|
|
782
|
+
if (k !== "FirstWriterWins" && k !== "FirstWriterCandidates" && k !== "Lww" && k !== "LwwCausal" && k !== "LwwBy") {
|
|
773
783
|
throw new Error(
|
|
774
784
|
`valueObject '${name}': .merge(${k}) is not a valid value-object field merge — a VO is ONE ` +
|
|
775
785
|
`wholesale json leaf, so only FirstWriterWins / Lww / LwwCausal / LwwBy apply. Field-level ` +
|
|
@@ -867,6 +877,15 @@ type UnionValue<Arms extends Record<string, UnionArmShape>> = {
|
|
|
867
877
|
[K in keyof Arms & string]: { type: K } & UnionArmValue<Arms[K]>;
|
|
868
878
|
}[keyof Arms & string];
|
|
869
879
|
|
|
880
|
+
/** Construction provenance is compile-time only; the kernel validates the declared
|
|
881
|
+
* nominal type from authenticated law, never from a caller-supplied brand. */
|
|
882
|
+
declare const NOMINAL_CASE: unique symbol;
|
|
883
|
+
export type NominalCase<Name extends string, T> = T & { readonly [NOMINAL_CASE]: Name };
|
|
884
|
+
export type VariantCases<Name extends string, Arms extends Record<string, UnionArmShape>> = {
|
|
885
|
+
readonly [K in keyof Arms & string]: (value: UnionArmValue<Arms[K]>) =>
|
|
886
|
+
NominalCase<Name, { type: K } & UnionArmValue<Arms[K]>>;
|
|
887
|
+
};
|
|
888
|
+
|
|
870
889
|
/**
|
|
871
890
|
* Declare a NAMED SUM TYPE (`variant("Shape", { circle: {...}, rect: {...} })`). The blessed,
|
|
872
891
|
* FIXED discriminator is `type` (the geo/RFC 7946 convention); its value is the arm key.
|
|
@@ -882,7 +901,7 @@ export function variant<const Name extends string, Arms extends Record<string, U
|
|
|
882
901
|
name: Name,
|
|
883
902
|
arms: Arms,
|
|
884
903
|
opts?: { example?: UnionValue<Arms> },
|
|
885
|
-
): ValueObject<UnionValue<Arms>> {
|
|
904
|
+
): ValueObject<UnionValue<Arms>> & { readonly name: Name; readonly cases: VariantCases<Name, Arms> } {
|
|
886
905
|
const armKeys = Object.keys(arms).sort();
|
|
887
906
|
if (armKeys.length === 0) throw new Error(`variant '${name}': at least one arm is required.`);
|
|
888
907
|
const deps = new Map<string, ValueObjectDescriptor>();
|
|
@@ -935,7 +954,12 @@ export function variant<const Name extends string, Arms extends Record<string, U
|
|
|
935
954
|
const zodSchema = (armZods.length === 1
|
|
936
955
|
? armZods[0]!
|
|
937
956
|
: z.discriminatedUnion("type", armZods as [z.ZodObject<z.ZodRawShape>, ...z.ZodObject<z.ZodRawShape>[]])) as unknown as z.ZodType<UnionValue<Arms>>;
|
|
938
|
-
|
|
957
|
+
const cases = Object.fromEntries(armKeys.map((type) => [type,
|
|
958
|
+
(value: object) => ({ ...value, type }),
|
|
959
|
+
])) as unknown as VariantCases<Name, Arms>;
|
|
960
|
+
return Object.assign(makeValueObject(name, zodSchema, descriptor, dependencies, undefined, opts?.example), {
|
|
961
|
+
name, cases: Object.freeze(cases),
|
|
962
|
+
});
|
|
939
963
|
}
|
|
940
964
|
|
|
941
965
|
// ── geo.* — the RFC 7946 geometry value objects ──────────────────────────────────────
|
|
@@ -1073,6 +1097,52 @@ export function collectPayloadValueObjectDescriptors(
|
|
|
1073
1097
|
return [...byName.values()].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
1074
1098
|
}
|
|
1075
1099
|
|
|
1100
|
+
/** Collect every named value object reachable from arbitrary public Zod
|
|
1101
|
+
* surfaces (notably derived/combined read results). This is the same nominal
|
|
1102
|
+
* vocabulary collector used for stored fields and directive payloads; a value
|
|
1103
|
+
* object does not stop being a public type merely because it is read-only. */
|
|
1104
|
+
export function collectSchemaValueObjectDescriptors(
|
|
1105
|
+
schemas: readonly unknown[],
|
|
1106
|
+
): ValueObjectDescriptor[] {
|
|
1107
|
+
const byName = new Map<string, ValueObjectDescriptor>();
|
|
1108
|
+
const take = (meta: ValueObjectFieldMeta | undefined): void => {
|
|
1109
|
+
if (meta === undefined) return;
|
|
1110
|
+
for (const descriptor of [meta.descriptor, ...meta.dependencies]) {
|
|
1111
|
+
const existing = byName.get(descriptor.name);
|
|
1112
|
+
if (existing !== undefined && JSON.stringify(existing) !== JSON.stringify(descriptor)) {
|
|
1113
|
+
throw new Error(
|
|
1114
|
+
`value object '${descriptor.name}' has DIVERGENT shapes across public schemas — one name, one shape.`,
|
|
1115
|
+
);
|
|
1116
|
+
}
|
|
1117
|
+
byName.set(descriptor.name, descriptor);
|
|
1118
|
+
}
|
|
1119
|
+
};
|
|
1120
|
+
const visit = (candidate: unknown, active: Set<z.ZodTypeAny>): void => {
|
|
1121
|
+
if (typeof candidate !== "object" || candidate === null) return;
|
|
1122
|
+
const schema = candidate as z.ZodTypeAny;
|
|
1123
|
+
if (active.has(schema)) return;
|
|
1124
|
+
const next = new Set(active).add(schema);
|
|
1125
|
+
take(voMetaOf(schema));
|
|
1126
|
+
const kind = zodKind(schema);
|
|
1127
|
+
const def = zodDef(schema);
|
|
1128
|
+
if (kind === "optional" || kind === "default" || kind === "nullable") {
|
|
1129
|
+
visit(def.innerType, next);
|
|
1130
|
+
} else if (kind === "array") {
|
|
1131
|
+
visit(zodArrayElement(schema), next);
|
|
1132
|
+
} else if (kind === "record") {
|
|
1133
|
+
visit(def.valueType, next);
|
|
1134
|
+
} else if (kind === "object") {
|
|
1135
|
+
for (const value of Object.values(zodObjectShape(schema))) visit(value, next);
|
|
1136
|
+
} else if (kind === "union") {
|
|
1137
|
+
for (const value of def.options ?? []) visit(value, next);
|
|
1138
|
+
}
|
|
1139
|
+
};
|
|
1140
|
+
for (const schema of schemas) visit(schema, new Set());
|
|
1141
|
+
return [...byName.values()].sort((a, b) =>
|
|
1142
|
+
a.name < b.name ? -1 : a.name > b.name ? 1 : 0,
|
|
1143
|
+
);
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1076
1146
|
export function collectValueObjectDescriptors(
|
|
1077
1147
|
aggregates: readonly { id: string; fields: Record<string, Field> }[],
|
|
1078
1148
|
): ValueObjectDescriptor[] {
|
package/src/wire.ts
CHANGED
|
@@ -45,6 +45,7 @@ export type WireValue =
|
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
47
|
| { Int: number }
|
|
48
|
+
| { ValueObject: WireValueObject }
|
|
48
49
|
| { Set: string[] }
|
|
49
50
|
| { Map: Record<string, WireField> }
|
|
50
51
|
| {
|
|
@@ -72,6 +73,24 @@ export type WireValue =
|
|
|
72
73
|
| { Position: string }
|
|
73
74
|
| { TwoPhaseSet: { adds: string[]; removes: string[] } };
|
|
74
75
|
|
|
76
|
+
/** Recursive, nominal application value carried by a value-object field. This is the plan ABI's
|
|
77
|
+
* typed value algebra, not a serialized document: the kernel converts it directly from QuickJS
|
|
78
|
+
* objects into nested USDA prims without JSON stringify/parse or a generic map at the public API. */
|
|
79
|
+
export interface WireValueObject {
|
|
80
|
+
readonly typeName: string;
|
|
81
|
+
readonly fields: Readonly<Record<string, WireNominalValue>>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export type WireNominalValue =
|
|
85
|
+
| { Null: true }
|
|
86
|
+
| { String: string }
|
|
87
|
+
| { Bool: boolean }
|
|
88
|
+
| { Int: number }
|
|
89
|
+
| { Double: number }
|
|
90
|
+
| { List: readonly WireNominalValue[] }
|
|
91
|
+
| { Map: Readonly<Record<string, WireNominalValue>> }
|
|
92
|
+
| { Object: WireValueObject };
|
|
93
|
+
|
|
75
94
|
export interface WireOwnedDeviceRequest {
|
|
76
95
|
v: number;
|
|
77
96
|
workspace: string;
|
|
@@ -201,6 +220,8 @@ export interface WireIntent {
|
|
|
201
220
|
| { readonly verifiedParent: true }
|
|
202
221
|
| { readonly verifiedSource: true };
|
|
203
222
|
targetCallUsda: Uint8Array;
|
|
223
|
+
/** Positions of selected earlier effects. The kernel resolves these to sealed digests. */
|
|
224
|
+
dependsOn?: readonly number[];
|
|
204
225
|
}[];
|
|
205
226
|
}
|
|
206
227
|
|
|
@@ -266,6 +287,7 @@ export interface WireReadAttestation {
|
|
|
266
287
|
*/
|
|
267
288
|
export type WireDriver =
|
|
268
289
|
| "FirstWriterWins"
|
|
290
|
+
| "FirstWriterCandidates"
|
|
269
291
|
| "Lww"
|
|
270
292
|
| "LwwCausal"
|
|
271
293
|
| "AddWins"
|
package/src/wire_encode.ts
CHANGED
|
@@ -40,12 +40,19 @@ import {
|
|
|
40
40
|
OWNED_PREMINTED_RECORDS_PAYLOAD_FIELD,
|
|
41
41
|
type AggregateHandle,
|
|
42
42
|
} from "./aggregate.js";
|
|
43
|
-
import {
|
|
43
|
+
import {
|
|
44
|
+
admitsNull,
|
|
45
|
+
type Field,
|
|
46
|
+
type EvidenceRefValue,
|
|
47
|
+
type ValueObjectDescriptor,
|
|
48
|
+
type ValueObjectFieldDescriptor,
|
|
49
|
+
type ValueObjectFieldMeta,
|
|
50
|
+
} from "./fields.js";
|
|
44
51
|
import type { Directive, ReferentialMarker } from "./directive.js";
|
|
45
|
-
import { isBusinessIntent } from "./intent.js";
|
|
52
|
+
import { isBusinessIntent, validateWorkspaceEffectDependencies } from "./intent.js";
|
|
46
53
|
import type { PlannedOp, FieldOp } from "./ops.js";
|
|
47
54
|
import { deterministicPorts, type Ports } from "./ctx.js";
|
|
48
|
-
import type
|
|
55
|
+
import { endorsementOf, type PrincipalFacet } from "./principal_evidence.js";
|
|
49
56
|
import type {
|
|
50
57
|
WireEvent,
|
|
51
58
|
WireHlc,
|
|
@@ -55,6 +62,8 @@ import type {
|
|
|
55
62
|
WireRefMode,
|
|
56
63
|
WireSchema,
|
|
57
64
|
WireValue,
|
|
65
|
+
WireNominalValue,
|
|
66
|
+
WireValueObject,
|
|
58
67
|
} from "./wire.js";
|
|
59
68
|
import {
|
|
60
69
|
zodArrayElement,
|
|
@@ -113,6 +122,148 @@ function describeShape(value: unknown): string {
|
|
|
113
122
|
return typeof value;
|
|
114
123
|
}
|
|
115
124
|
|
|
125
|
+
function nominalRecordEntries(value: unknown, at: string): Record<string, unknown> {
|
|
126
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
127
|
+
throw new TypeError(`${at} must be an object`);
|
|
128
|
+
}
|
|
129
|
+
return value as Record<string, unknown>;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function encodeUntypedNominal(value: unknown, at: string): WireNominalValue {
|
|
133
|
+
if (typeof value === "string") return { String: value };
|
|
134
|
+
if (typeof value === "boolean") return { Bool: value };
|
|
135
|
+
if (typeof value === "number") {
|
|
136
|
+
if (!Number.isFinite(value)) throw new TypeError(`${at} must be finite`);
|
|
137
|
+
if (Number.isInteger(value)) {
|
|
138
|
+
if (!Number.isSafeInteger(value)) throw new TypeError(`${at} is outside JavaScript's exact integer range`);
|
|
139
|
+
return { Int: value };
|
|
140
|
+
}
|
|
141
|
+
return { Double: value };
|
|
142
|
+
}
|
|
143
|
+
if (Array.isArray(value)) {
|
|
144
|
+
return { List: value.map((member, index) => encodeUntypedNominal(member, `${at}[${index}]`)) };
|
|
145
|
+
}
|
|
146
|
+
const object = nominalRecordEntries(value, at);
|
|
147
|
+
return {
|
|
148
|
+
Map: Object.fromEntries(
|
|
149
|
+
Object.entries(object)
|
|
150
|
+
.filter(([, member]) => member !== undefined)
|
|
151
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
152
|
+
.map(([key, member]) => [key, encodeUntypedNominal(member, `${at}.${key}`)]),
|
|
153
|
+
),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function encodeDescribedNominal(
|
|
158
|
+
descriptor: ValueObjectFieldDescriptor,
|
|
159
|
+
value: unknown,
|
|
160
|
+
types: ReadonlyMap<string, ValueObjectDescriptor>,
|
|
161
|
+
at: string,
|
|
162
|
+
): WireNominalValue {
|
|
163
|
+
if (value === null) {
|
|
164
|
+
if (descriptor.nullable === true) return { Null: true };
|
|
165
|
+
throw new TypeError(`${at} does not admit null`);
|
|
166
|
+
}
|
|
167
|
+
switch (descriptor.type) {
|
|
168
|
+
case "string":
|
|
169
|
+
case "enum":
|
|
170
|
+
case "literal":
|
|
171
|
+
if (typeof value !== "string") throw new TypeError(`${at} must be a string`);
|
|
172
|
+
return { String: value };
|
|
173
|
+
case "boolean":
|
|
174
|
+
if (typeof value !== "boolean") throw new TypeError(`${at} must be a boolean`);
|
|
175
|
+
return { Bool: value };
|
|
176
|
+
case "number":
|
|
177
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new TypeError(`${at} must be a finite number`);
|
|
178
|
+
if (descriptor.int === true) {
|
|
179
|
+
if (!Number.isSafeInteger(value)) throw new TypeError(`${at} must be an exact integer`);
|
|
180
|
+
return { Int: value };
|
|
181
|
+
}
|
|
182
|
+
return { Double: value };
|
|
183
|
+
case "array":
|
|
184
|
+
if (!Array.isArray(value)) throw new TypeError(`${at} must be an array`);
|
|
185
|
+
return { List: value.map((member, index) => encodeDescribedNominal(descriptor.items, member, types, `${at}[${index}]`)) };
|
|
186
|
+
case "map": {
|
|
187
|
+
const object = nominalRecordEntries(value, at);
|
|
188
|
+
return {
|
|
189
|
+
Map: Object.fromEntries(
|
|
190
|
+
Object.entries(object)
|
|
191
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
192
|
+
.map(([key, member]) => [key, encodeDescribedNominal(descriptor.values, member, types, `${at}.${key}`)]),
|
|
193
|
+
),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
case "object":
|
|
197
|
+
case "geometry": {
|
|
198
|
+
const target = types.get(descriptor.ref);
|
|
199
|
+
if (target === undefined) throw new TypeError(`${at} references unknown value object '${descriptor.ref}'`);
|
|
200
|
+
return { Object: encodeNominalValueObject(target, value, types, at) };
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** Internal structural encoder, also used by independent storage-codec fixtures.
|
|
206
|
+
* Not a validator or admission API; callers must validate authored domain data.
|
|
207
|
+
*/
|
|
208
|
+
export function encodeNominalValueObject(
|
|
209
|
+
descriptor: ValueObjectDescriptor,
|
|
210
|
+
value: unknown,
|
|
211
|
+
types: ReadonlyMap<string, ValueObjectDescriptor>,
|
|
212
|
+
at = descriptor.name,
|
|
213
|
+
): WireValueObject {
|
|
214
|
+
const object = nominalRecordEntries(value, at);
|
|
215
|
+
if (descriptor.fields !== undefined) {
|
|
216
|
+
return {
|
|
217
|
+
typeName: descriptor.name,
|
|
218
|
+
fields: Object.fromEntries(
|
|
219
|
+
Object.entries(descriptor.fields)
|
|
220
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
221
|
+
.flatMap(([key, member]) => {
|
|
222
|
+
const fieldValue = object[key];
|
|
223
|
+
if (fieldValue === undefined && member.optional === true) return [];
|
|
224
|
+
return [[key, encodeDescribedNominal(member, fieldValue, types, `${at}.${key}`)] as const];
|
|
225
|
+
}),
|
|
226
|
+
),
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
if (descriptor.union !== undefined) {
|
|
230
|
+
const discriminator = object["type"];
|
|
231
|
+
if (typeof discriminator !== "string") throw new TypeError(`${at}.type must be a string discriminator`);
|
|
232
|
+
const arm = descriptor.union[discriminator];
|
|
233
|
+
if (arm === undefined) throw new TypeError(`${at}.type '${discriminator}' is not a declared arm`);
|
|
234
|
+
const fields: Record<string, WireNominalValue> = { type: { String: discriminator } };
|
|
235
|
+
if ("ref" in arm) {
|
|
236
|
+
const target = types.get(arm.ref);
|
|
237
|
+
if (target === undefined) throw new TypeError(`${at} references unknown value object '${arm.ref}'`);
|
|
238
|
+
fields["value"] = { Object: encodeNominalValueObject(target, object["value"], types, `${at}.value`) };
|
|
239
|
+
} else {
|
|
240
|
+
for (const [key, member] of Object.entries(arm.fields).sort(([left], [right]) => left.localeCompare(right))) {
|
|
241
|
+
const fieldValue = object[key];
|
|
242
|
+
if (fieldValue === undefined && member.optional === true) continue;
|
|
243
|
+
fields[key] = encodeDescribedNominal(member, fieldValue, types, `${at}.${key}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return { typeName: descriptor.name, fields };
|
|
247
|
+
}
|
|
248
|
+
// Geometry has a standard fixed shape. Preserve its actual scalar/list/map kinds while retaining
|
|
249
|
+
// the declared RFC 7946 nominal type at the enclosing prim.
|
|
250
|
+
return {
|
|
251
|
+
typeName: descriptor.name,
|
|
252
|
+
fields: Object.fromEntries(
|
|
253
|
+
Object.entries(object)
|
|
254
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
255
|
+
.map(([key, member]) => [key, encodeUntypedNominal(member, `${at}.${key}`)]),
|
|
256
|
+
),
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function encodeValueObject(meta: ValueObjectFieldMeta, value: unknown): WireValueObject {
|
|
261
|
+
const types = new Map(
|
|
262
|
+
[meta.descriptor, ...meta.dependencies].map((descriptor) => [descriptor.name, descriptor] as const),
|
|
263
|
+
);
|
|
264
|
+
return encodeNominalValueObject(meta.descriptor, value, types);
|
|
265
|
+
}
|
|
266
|
+
|
|
116
267
|
/** Map an authored op value onto the kernel `Value` shape, given the field kind.
|
|
117
268
|
* `op` is passed purely for a NAMED diagnostic (aggregate + field + expected VO type) when a value
|
|
118
269
|
* can't be lowered — never `field '?'`. */
|
|
@@ -186,6 +337,9 @@ function encodeKernelValue(field: Field | undefined, value: unknown, op?: FieldO
|
|
|
186
337
|
if (field?.kind === "json" && voMetaOf(field.zod)?.name === OwnedDeviceRequestValue.name) {
|
|
187
338
|
return { OwnedDeviceRequest: OwnedDeviceRequestValue.zod.parse(value) };
|
|
188
339
|
}
|
|
340
|
+
if (field?.valueObject !== undefined) {
|
|
341
|
+
return { ValueObject: encodeValueObject(field.valueObject, field.zod.parse(value)) };
|
|
342
|
+
}
|
|
189
343
|
// A `t.json()` leaf lowers to the FIRST-CLASS kernel `Value::Json` (RFC 8785 / JCS), NOT a
|
|
190
344
|
// `Value::Str` holding JSON-shaped text. Checked BEFORE the scalar fallthroughs so EVERY json
|
|
191
345
|
// value — object, array, string, number, boolean, null — commits as one consistent value kind
|
|
@@ -222,6 +376,9 @@ function encodeKernelValue(field: Field | undefined, value: unknown, op?: FieldO
|
|
|
222
376
|
// entries, which the kernel's authz fold parses as Str. Routing strings to Json broke
|
|
223
377
|
// relation seating repo-wide (warrant_flip/va_core/relation-bootstrap all red). Objects were
|
|
224
378
|
// previously a REFUSAL here, so giving them the Json lane changes only the case that threw.
|
|
379
|
+
if (field?.kind === "map" && field.mapValueVo !== undefined) {
|
|
380
|
+
return { ValueObject: encodeValueObject(field.mapValueVo, value) };
|
|
381
|
+
}
|
|
225
382
|
if (field?.kind === "map" && field.mapValueKind === "json" && typeof value === "object" && value !== null) {
|
|
226
383
|
return { Json: value };
|
|
227
384
|
}
|
|
@@ -243,14 +400,11 @@ function encodeKernelValue(field: Field | undefined, value: unknown, op?: FieldO
|
|
|
243
400
|
`(instance/existing/ensure(TargetAgg, id)) so the field kind is known.`,
|
|
244
401
|
);
|
|
245
402
|
}
|
|
246
|
-
// Name the expected VO: a top-level VO field carries it on `valueObject`, a VO-valued MAP entry on
|
|
247
|
-
// `mapValueVo` (the inner value's VO metadata) — so a bad `t.map(VO.field())` entry names its VO too.
|
|
248
|
-
const expected = field.valueObject?.name ?? field.mapValueVo?.name;
|
|
249
403
|
const kindLabel = field.kind === "map" ? `map (${field.mapValueKind ?? "?"}-valued)` : field.kind;
|
|
250
404
|
throw new Error(
|
|
251
|
-
`${aggName}.${fieldName} is a '${kindLabel}' field
|
|
405
|
+
`${aggName}.${fieldName} is a '${kindLabel}' field, ` +
|
|
252
406
|
`but got ${describeShape(value)} (${JSON.stringify(value)}) — kernel scalars are Str | Bool | Int. ` +
|
|
253
|
-
`A structured value needs a declared value-object field; a set/map needs its
|
|
407
|
+
`A structured value needs a declared value-object field; a set/map needs its structural write.`,
|
|
254
408
|
);
|
|
255
409
|
}
|
|
256
410
|
|
|
@@ -320,7 +474,7 @@ function encodeKernelFieldOp(op: FieldOp, field: Field | undefined): WireFieldOp
|
|
|
320
474
|
* to test domain LIFECYCLE.
|
|
321
475
|
*
|
|
322
476
|
* For a KERNEL-BACKED local harness — admission through the real gate, folds,
|
|
323
|
-
* reads, minted ids, the whole lifecycle on the same
|
|
477
|
+
* reads, minted ids, the whole lifecycle on the same kernel host the cloud
|
|
324
478
|
* edge runs — use the CLI, NOT this function:
|
|
325
479
|
* • `githolon dev --once` — ONE compile → law-live-locally → proof cycle, then
|
|
326
480
|
* exit (the CI/smoke lane for lifecycle + id-mint tests — a non-zero exit
|
|
@@ -418,20 +572,35 @@ export function executeDirectiveToIntent<P>(
|
|
|
418
572
|
for (const b of births) meterOps.push(...planBirthRecordOps(b, subject, bornAt));
|
|
419
573
|
}
|
|
420
574
|
const planned = [...__drainAuthoring(), ...returned, ...meterOps];
|
|
421
|
-
const
|
|
422
|
-
|
|
575
|
+
const declaredWorkspaceEffects = directive.workspaceEffects ?? [];
|
|
576
|
+
// Resolve names before filtering: a conditional predecessor that did not run must never
|
|
577
|
+
// disappear from the dependency graph and accidentally authorize its dependent.
|
|
578
|
+
validateWorkspaceEffectDependencies(declaredWorkspaceEffects);
|
|
579
|
+
const selectedWorkspaceEffects = declaredWorkspaceEffects
|
|
580
|
+
.filter(effect => effect.when?.(parsed, ctx) ?? true);
|
|
581
|
+
const selectedEffectIndices = new Map(selectedWorkspaceEffects
|
|
582
|
+
.flatMap((effect, index) => effect.key === undefined ? [] : [[effect.key, index] as const]));
|
|
583
|
+
const workspaceEffects = selectedWorkspaceEffects
|
|
584
|
+
.map((effect) => {
|
|
585
|
+
const dependsOn = (effect.after ?? []).map(key => {
|
|
586
|
+
const index = selectedEffectIndices.get(key);
|
|
587
|
+
if (index === undefined) {
|
|
588
|
+
throw new Error(`Directive '${directive.id}' requires workspace effect '${key}', but its condition did not select it`);
|
|
589
|
+
}
|
|
590
|
+
return index;
|
|
591
|
+
}).sort((a, b) => a - b);
|
|
423
592
|
if (encodeWorkspaceEffect === undefined) {
|
|
424
593
|
throw new Error(
|
|
425
594
|
`Directive '${directive.id}' declares a workspace effect, but its compiled plan has no target-call encoder`,
|
|
426
595
|
);
|
|
427
596
|
}
|
|
428
597
|
const workspace = effect.destination.kind === "workspace"
|
|
429
|
-
? effect.destination.address(parsed)
|
|
598
|
+
? effect.destination.address(parsed, ctx)
|
|
430
599
|
: "";
|
|
431
600
|
if (effect.destination.kind === "workspace" && workspace.trim().length === 0) {
|
|
432
601
|
throw new Error(`Directive '${directive.id}' produced an empty target workspace address`);
|
|
433
602
|
}
|
|
434
|
-
const targetPayload = effect.target.payloadSchema.parse(effect.payload(parsed));
|
|
603
|
+
const targetPayload = effect.target.payloadSchema.parse(effect.payload(parsed, ctx));
|
|
435
604
|
return {
|
|
436
605
|
destination: effect.destination.kind === "workspace"
|
|
437
606
|
? { workspace }
|
|
@@ -443,9 +612,9 @@ export function executeDirectiveToIntent<P>(
|
|
|
443
612
|
targetPayload,
|
|
444
613
|
workspace,
|
|
445
614
|
),
|
|
615
|
+
...(dependsOn.length ? { dependsOn } : {}),
|
|
446
616
|
};
|
|
447
|
-
})
|
|
448
|
-
: [];
|
|
617
|
+
});
|
|
449
618
|
// Workspace-effect payload mappers are source law and may perform declared captured reads while
|
|
450
619
|
// constructing the target payload. Drain only after both local and cross-workspace consequences.
|
|
451
620
|
const reads = __drainReads();
|
|
@@ -525,14 +694,25 @@ export function executeDirectiveToIntent<P>(
|
|
|
525
694
|
bucket.push(encodeKernelFieldOp(op, field));
|
|
526
695
|
}
|
|
527
696
|
|
|
697
|
+
// A `.creates` directive that also declares `.alsoMutates` of its own type (a replacement that supersedes
|
|
698
|
+
// the row it replaces) addresses TWO rows of that type: the one `create()` marks, and an existing one it
|
|
699
|
+
// reaches with `existing(...)`. Only the marked row is created; the other is the declared mutate, or the
|
|
700
|
+
// referential gate refuses it as a create of an id that already exists.
|
|
701
|
+
const mutatesOwnType =
|
|
702
|
+
directive.marker === "creates" &&
|
|
703
|
+
(directive.secondaryOutputs ?? []).some(
|
|
704
|
+
(output) => output.marker === "mutates" && output.aggregateId === agg.id,
|
|
705
|
+
) &&
|
|
706
|
+
order.some((key) => ownTarget.get(key) === true && explicitMarker.get(key) === "Create");
|
|
528
707
|
const events: WireEvent[] = order.map((bucketKey) => {
|
|
529
708
|
const aggregate = idOf.get(bucketKey)!;
|
|
530
709
|
const ops = byAggregate.get(bucketKey)!;
|
|
531
710
|
const ty = typeOf.get(bucketKey);
|
|
532
711
|
// The directive's marker applies to its OWN target; a FAN-OUT bucket is a Mutate of an
|
|
533
712
|
// existing sibling aggregate (so the id-mint gate never sees a spurious Create for it).
|
|
713
|
+
const ownMarker = mutatesOwnType && ty !== undefined ? "Mutate" : marker;
|
|
534
714
|
const eventMarker: WireRefMode =
|
|
535
|
-
explicitMarker.get(bucketKey) ?? (ownTarget.get(bucketKey) ?
|
|
715
|
+
explicitMarker.get(bucketKey) ?? (ownTarget.get(bucketKey) ? ownMarker : "Mutate");
|
|
536
716
|
if (ty !== undefined) {
|
|
537
717
|
// Stamp provenance FIRST so the fold carries the TYPE + canonical id the
|
|
538
718
|
// structured `view()` projects (the same `__type`/`__id` reserved fields the
|
|
@@ -624,6 +804,14 @@ function principalFlowProbeValue(
|
|
|
624
804
|
}
|
|
625
805
|
}
|
|
626
806
|
|
|
807
|
+
/** Does `value` hold principal evidence minted by THIS DSL instance anywhere inside it? */
|
|
808
|
+
function carriesUnloweredEvidence(value: unknown, seen = new Set<object>()): boolean {
|
|
809
|
+
if (typeof value !== "object" || value === null || seen.has(value)) return false;
|
|
810
|
+
seen.add(value);
|
|
811
|
+
if (endorsementOf(value) !== null) return true;
|
|
812
|
+
return Object.values(value as Record<string, unknown>).some(inner => carriesUnloweredEvidence(inner, seen));
|
|
813
|
+
}
|
|
814
|
+
|
|
627
815
|
/** Extract the principal flows carried by a plan's field ops (those with an `endorsement`). The ONE
|
|
628
816
|
* op→flow mapping shared by the discovery harvest (compile) and the runtime footprint (Slice B). */
|
|
629
817
|
function flowsFromOps(ops: readonly FieldOp[]): PrincipalFlow[] {
|
|
@@ -646,8 +834,14 @@ function flowsFromOps(ops: readonly FieldOp[]): PrincipalFlow[] {
|
|
|
646
834
|
* the omission is SAFE because the runtime/replay witness (Slice B) refuses any observed flow absent
|
|
647
835
|
* from the manifest. The reasons are static law, so they are intent-independent. SORTED for determinism.
|
|
648
836
|
*/
|
|
649
|
-
export function harvestPrincipalFlows(directive: Directive<
|
|
650
|
-
|
|
837
|
+
export function harvestPrincipalFlows<P>(directive: Directive<P>, domain?: string): PrincipalFlow[] {
|
|
838
|
+
// Every composed local step is part of the declared law. A later captured read
|
|
839
|
+
// can stop a synthetic probe; it must not erase flows already declared by an
|
|
840
|
+
// earlier step (notably the invitation's possession-proven authority grant).
|
|
841
|
+
const stepFlows = isBusinessIntent(directive)
|
|
842
|
+
? directive.steps.flatMap(step => harvestPrincipalFlows(step.directive, domain)) : [];
|
|
843
|
+
const ctx: Ports = deterministicPorts({ principal: directive.acceptsVerifiedWorkspaceClaimGate
|
|
844
|
+
? "workspace:probe" : "user:probe" });
|
|
651
845
|
let candidate: unknown;
|
|
652
846
|
try {
|
|
653
847
|
candidate = principalFlowProbeValue(directive.payloadSchema as z.ZodTypeAny);
|
|
@@ -696,11 +890,24 @@ export function harvestPrincipalFlows(directive: Directive<unknown>, domain?: st
|
|
|
696
890
|
ops = [...__drainAuthoring(), ...(Array.isArray(returned) ? returned : [])].filter((op): op is FieldOp => op.kind !== "strike");
|
|
697
891
|
} catch {
|
|
698
892
|
try { __drainAuthoring(); __drainReads(); } catch { /* sink already drained */ }
|
|
699
|
-
return
|
|
893
|
+
return stepFlows;
|
|
700
894
|
} finally {
|
|
701
895
|
if (domain !== undefined) __setDispatchDomain(undefined);
|
|
702
896
|
if (hadNomos && prior !== undefined) g.nomos = prior; else delete g.nomos;
|
|
703
897
|
}
|
|
704
898
|
__drainReads();
|
|
705
|
-
|
|
899
|
+
const ownFlows = flowsFromOps(ops);
|
|
900
|
+
// AN UNHARVESTABLE ENDORSEMENT IS AN ERROR, not a weaker law. Principal evidence the plan converted
|
|
901
|
+
// (asAuthoritySubject/asStandingParty) is lowered to its raw principal by the framework before it
|
|
902
|
+
// reaches an op. Evidence still inside an op was never lowered: the plan ran against a second
|
|
903
|
+
// @githolon/dsl instance whose registry cannot see this one's endorsements, so the flow would be
|
|
904
|
+
// missing from the compiled manifest.
|
|
905
|
+
if (ops.some(op => carriesUnloweredEvidence(op))) {
|
|
906
|
+
throw new Error(
|
|
907
|
+
`principal-flow discovery for '${directive.id}' found converted principal evidence its plan never ` +
|
|
908
|
+
`lowered: the plan ran against a second @githolon/dsl instance, so its endorsed flows cannot be harvested`,
|
|
909
|
+
);
|
|
910
|
+
}
|
|
911
|
+
return [...stepFlows, ...ownFlows].filter((flow, index, all) =>
|
|
912
|
+
all.findIndex(candidate => candidate.sink === flow.sink && candidate.facet === flow.facet && candidate.reason === flow.reason) === index);
|
|
706
913
|
}
|
|
@@ -79,6 +79,7 @@ import { query } from "./query.js";
|
|
|
79
79
|
import { read } from "./read.js";
|
|
80
80
|
import { sum } from "./sum.js";
|
|
81
81
|
import { min, max } from "./extremum.js";
|
|
82
|
+
import { valueObject } from "./value_object.js";
|
|
82
83
|
import {
|
|
83
84
|
idItem,
|
|
84
85
|
idPayload,
|
|
@@ -439,18 +440,26 @@ export const nomosReceiveAssignment = directive("nomosReceiveAssignment")
|
|
|
439
440
|
* receipt-leg obligation ALONGSIDE the unchanged assignment flip; the host orchestration still runs
|
|
440
441
|
* the move (the obligation is observable on the outbox, not yet consumed). `architecture/sharding_as_outbound_offers.md`.
|
|
441
442
|
*/
|
|
443
|
+
export const NomosMoveHomeKeys = valueObject("NomosMoveHomeKeys", {
|
|
444
|
+
values: z.array(z.string().min(1)).min(1).max(64),
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
export const NomosMigratedIntentIds = valueObject("NomosMigratedIntentIds", {
|
|
448
|
+
values: z.array(z.string().min(1)).max(1024),
|
|
449
|
+
});
|
|
450
|
+
|
|
442
451
|
export const NomosMoveObligation = impureCapabilityAggregate("NomosMoveObligation", {
|
|
443
452
|
// the move-leg params (sealed as typed USDA values in the outbound offer):
|
|
444
453
|
fromShard: t.string().merge(Lww),
|
|
445
454
|
toShard: t.string().merge(Lww),
|
|
446
|
-
homeKeys:
|
|
455
|
+
homeKeys: NomosMoveHomeKeys.field().merge(Lww), // the moving home keys (the leg's work list)
|
|
447
456
|
leg: t.string().merge(Lww), // one of MOVE_LEGS
|
|
448
457
|
mapVersion: t.int().merge(Lww), // the law-derived map version (S3a), ferried leg-to-leg
|
|
449
458
|
// S3b — the target shard's ADMITTED set, accumulated migrate→seal (the cross-leg state seal-home
|
|
450
459
|
// needs to compute `missing` against the source chain). The LAW unions it (the adapter only ferries
|
|
451
460
|
// arrays it read off the obligation + the shard response — it never accumulates), so the host holds no
|
|
452
461
|
// cross-leg move state and an adapter restart re-derives everything from the on-chain obligation.
|
|
453
|
-
migratedIds:
|
|
462
|
+
migratedIds: NomosMigratedIntentIds.field().merge(Lww), // empty until the migrate leg's receipt
|
|
454
463
|
});
|
|
455
464
|
|
|
456
465
|
/** S1 receipt-leg obligation window (ms) — the leg's soft deadline past its scheduledAt. */
|
|
@@ -509,10 +518,10 @@ export const nomosSplitShard = directive("nomosSplitShard")
|
|
|
509
518
|
.set("status", "requested")
|
|
510
519
|
.set("fromShard", p.fromShard)
|
|
511
520
|
.set("toShard", p.toShard)
|
|
512
|
-
.set("homeKeys",
|
|
521
|
+
.set("homeKeys", { values: p.homeKeys })
|
|
513
522
|
.set("leg", "receipt")
|
|
514
523
|
.set("mapVersion", newMapVersion)
|
|
515
|
-
.set("migratedIds",
|
|
524
|
+
.set("migratedIds", { values: [] }); // nothing migrated yet — the migrate leg's receipt accumulates it
|
|
516
525
|
return ops;
|
|
517
526
|
})
|
|
518
527
|
.reads(nomosShardAssignmentByHomeKey);
|
|
@@ -573,10 +582,10 @@ export const nomosAdvanceMove = directive("nomosAdvanceMove")
|
|
|
573
582
|
.set("status", "requested")
|
|
574
583
|
.set("fromShard", p.fromShard)
|
|
575
584
|
.set("toShard", p.toShard)
|
|
576
|
-
.set("homeKeys",
|
|
585
|
+
.set("homeKeys", { values: p.homeKeys })
|
|
577
586
|
.set("leg", nextLeg)
|
|
578
587
|
.set("mapVersion", p.mapVersion) // carry the law-derived version forward (S3a)
|
|
579
|
-
.set("migratedIds",
|
|
588
|
+
.set("migratedIds", { values: accMigrated }); // S3b — the accumulated admitted set
|
|
580
589
|
}
|
|
581
590
|
return ops;
|
|
582
591
|
});
|