@githolon/dsl 0.100.6 → 0.101.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/dart/CHANGELOG.md +6 -0
- package/dart/lib/src/dev_cloud.dart +46 -15
- package/dart/lib/src/test_harness.dart +78 -16
- package/dart/lib/src/webview_bridge.dart +640 -126
- package/dart/pubspec.lock +1 -1
- package/dart/pubspec.yaml +2 -2
- package/dart/test/bundle_version_pairing_test.dart +11 -22
- package/dart/test/co2_product_gauntlet_test.dart +228 -0
- package/dart/test/errors_test.dart +63 -12
- package/dart/test/first_app_gauntlet_test.dart +29 -3
- package/dart/test/webview_bridge_test.dart +274 -0
- package/dart_types/CHANGELOG.md +6 -0
- package/dart_types/lib/src/errors.dart +136 -30
- package/dart_types/lib/src/surfaced.dart +48 -2
- package/dart_types/lib/src/wire.dart +3 -1
- package/dart_types/pubspec.yaml +1 -1
- package/dart_types/test/surfaced_test.dart +19 -0
- package/package.json +1 -1
- package/src/activity.ts +43 -4
- package/src/aggregate.ts +132 -22
- package/src/attested_core.ts +1 -1
- package/src/authoring.ts +119 -6
- package/src/birth_meter.ts +4 -4
- package/src/birth_meter_ensure.ts +9 -1
- package/src/births.ts +53 -22
- package/src/build_package.ts +55 -14
- package/src/business_birth.ts +48 -7
- package/src/codegen_dart.ts +267 -37
- package/src/codegen_dart_client.ts +921 -82
- package/src/codegen_openapi.ts +28 -5
- package/src/codegen_proof.ts +107 -11
- package/src/codegen_ts.ts +961 -135
- package/src/compile_package_main.ts +155 -45
- package/src/conflicts.ts +252 -0
- package/src/describe_model.ts +448 -0
- package/src/directive.ts +5 -38
- package/src/drivers.ts +12 -4
- package/src/engine_entry.ts +4 -0
- package/src/evolve_preview.ts +72 -11
- package/src/fields.ts +201 -55
- package/src/framework/application_home.ts +103 -0
- package/src/framework/application_home_domain.ts +2 -0
- package/src/framework/attested_receipts.ts +2 -2
- package/src/framework/auth_provider_law.ts +51 -24
- package/src/framework/business_corrections.ts +170 -0
- package/src/framework/disclosure.ts +1 -1
- package/src/framework/domain_lifecycle.ts +250 -16
- package/src/framework/encryption_identity.ts +1 -1
- package/src/framework/encryption_scope.ts +21 -13
- package/src/framework/era_certification.ts +1 -1
- package/src/framework/framework_aggregates.ts +2 -0
- package/src/framework/genesis.ts +1 -1
- package/src/framework/home.ts +22 -9
- package/src/framework/identity.ts +5 -5
- package/src/framework/impure_capability.ts +2 -2
- package/src/framework/owned_custody.ts +561 -0
- package/src/framework/platform_admin.ts +1 -1
- package/src/framework/relations.ts +9 -1
- package/src/framework/repair.ts +1 -1
- package/src/framework/signing_identity.ts +2 -2
- package/src/framework/source_roster.ts +1 -1
- package/src/framework/workspaces.ts +17 -10
- package/src/generated_client_names.ts +39 -0
- package/src/index.ts +20 -15
- package/src/kernel_capabilities.ts +25 -1
- package/src/law_check.ts +4 -1
- package/src/manifest.ts +356 -63
- package/src/read_projection_manifest.ts +8 -0
- package/src/read_projection_namespace.ts +4 -0
- package/src/relation.ts +1 -1
- package/src/shape_compat.ts +47 -2
- package/src/stable_ids.ts +38 -6
- package/src/stable_ids_types.ts +30 -0
- package/src/surfaced_lint.ts +0 -0
- package/src/usd.ts +62 -11
- package/src/usd_layers.ts +54 -3
- package/src/value_object.ts +23 -19
- package/src/wire.ts +1 -0
- package/src/wire_encode.ts +48 -4
- package/src/workspace_routing.ts +57 -0
- package/src/workspace_sharding.ts +9 -9
- package/src/certified_read.ts +0 -313
package/src/stable_ids.ts
CHANGED
|
@@ -27,6 +27,11 @@
|
|
|
27
27
|
* Note the NESTING: field sids mint under the AGGREGATE SID (not its name), so a
|
|
28
28
|
* renamed aggregate's fields keep their identities for free.
|
|
29
29
|
*
|
|
30
|
+
* GLOBAL FRAMEWORK AGGREGATES are the one exception to domain-scoped minting. The same
|
|
31
|
+
* kernel-owned aggregate (for example the relation spine) is composed into several
|
|
32
|
+
* independently compiled domains, so it mints beneath a reserved global scope. Every
|
|
33
|
+
* package therefore publishes one identical field-contract identity for that shared row.
|
|
34
|
+
*
|
|
30
35
|
* CONTINUITY (across compiles): `nomos-compile` reads the PRIOR build's identity
|
|
31
36
|
* manifests / the committed `nomos.stable-ids.json` lockfile and derives a continuity
|
|
32
37
|
* table per domain — match by name first; then the UNAMBIGUOUS-PAIR rule: exactly ONE
|
|
@@ -48,7 +53,13 @@ import { createHash } from "node:crypto";
|
|
|
48
53
|
import type { WireSchema } from "./wire.js";
|
|
49
54
|
import type { StableAggregateId, StableFieldId, StableIds } from "./stable_ids_types.js";
|
|
50
55
|
|
|
51
|
-
export type {
|
|
56
|
+
export type {
|
|
57
|
+
StableAggregateId,
|
|
58
|
+
StableFieldEvolution,
|
|
59
|
+
StableFieldId,
|
|
60
|
+
StableFieldValueKind,
|
|
61
|
+
StableIds,
|
|
62
|
+
} from "./stable_ids_types.js";
|
|
52
63
|
|
|
53
64
|
/** Mint one stable id: first 16 hex chars of sha256 over the versioned scope. */
|
|
54
65
|
export function mintStableId(scope: string): string {
|
|
@@ -67,6 +78,11 @@ export function mintFieldSid(aggSid: string, fieldName: string): string {
|
|
|
67
78
|
return mintStableId(`field:${aggSid}:${fieldName}`);
|
|
68
79
|
}
|
|
69
80
|
|
|
81
|
+
/** Canonical scope for a framework aggregate shared by independently compiled domains. */
|
|
82
|
+
export function mintGlobalAggregateSid(aggName: string): string {
|
|
83
|
+
return mintStableId(`global-aggregate:${aggName}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
70
86
|
/** One inferred rename, printed by the compile summary (visibility for the silent lane). */
|
|
71
87
|
export interface InferredRename {
|
|
72
88
|
/** `"<Agg>"` for an aggregate rename, `"<Agg>.<field>"` for a field rename. */
|
|
@@ -93,7 +109,13 @@ export interface InferredRename {
|
|
|
93
109
|
*/
|
|
94
110
|
export function deriveStableIdContinuity(
|
|
95
111
|
domainName: string,
|
|
96
|
-
currentAggregates: {
|
|
112
|
+
currentAggregates: {
|
|
113
|
+
id: string;
|
|
114
|
+
schema: WireSchema;
|
|
115
|
+
kinds?: Record<string, string>;
|
|
116
|
+
/** The same framework-owned aggregate is composed into more than one domain. */
|
|
117
|
+
global?: boolean;
|
|
118
|
+
}[],
|
|
97
119
|
prior: StableIds | undefined,
|
|
98
120
|
/** The prior canonical manifest's aggregate schemas (keyed by PRIOR name) — when
|
|
99
121
|
* supplied, tightens the field-pair rule to same-driver only. */
|
|
@@ -111,6 +133,10 @@ export function deriveStableIdContinuity(
|
|
|
111
133
|
{ sid: string; was?: string[]; priorFields?: Record<string, StableFieldId>; priorSchema?: WireSchema }
|
|
112
134
|
> = {};
|
|
113
135
|
for (const agg of currentAggregates) {
|
|
136
|
+
if (agg.global === true) {
|
|
137
|
+
resolved[agg.id] = { sid: mintGlobalAggregateSid(agg.id), priorFields: {} };
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
114
140
|
const p = priorAggs[agg.id];
|
|
115
141
|
if (p !== undefined) {
|
|
116
142
|
resolved[agg.id] = {
|
|
@@ -148,14 +174,20 @@ export function deriveStableIdContinuity(
|
|
|
148
174
|
const aggregates: Record<string, StableAggregateId> = {};
|
|
149
175
|
for (const agg of currentAggregates) {
|
|
150
176
|
const r = resolved[agg.id];
|
|
151
|
-
const aggSid =
|
|
152
|
-
|
|
177
|
+
const aggSid =
|
|
178
|
+
agg.global === true
|
|
179
|
+
? mintGlobalAggregateSid(agg.id)
|
|
180
|
+
: r?.sid ?? mintAggregateSid(domainSid, agg.id);
|
|
181
|
+
const priorFields = agg.global === true ? {} : r?.priorFields ?? {};
|
|
153
182
|
const fieldNames = Object.keys(agg.schema);
|
|
154
183
|
const fields: Record<string, StableFieldId> = {};
|
|
155
184
|
for (const f of fieldNames) {
|
|
156
185
|
const pf = priorFields[f];
|
|
157
186
|
if (pf !== undefined) {
|
|
158
|
-
|
|
187
|
+
// Carry compiler-owned contract/evolution history into the continuity input. The
|
|
188
|
+
// final manifest re-derives the current contract, but needs this prior descriptor to
|
|
189
|
+
// prove automatic widening and retain routes from every still-supported era.
|
|
190
|
+
fields[f] = { ...pf };
|
|
159
191
|
}
|
|
160
192
|
}
|
|
161
193
|
const fDisappeared = Object.keys(priorFields).filter((n) => !fieldNames.includes(n));
|
|
@@ -179,7 +211,7 @@ export function deriveStableIdContinuity(
|
|
|
179
211
|
if (candidates.length === 1) {
|
|
180
212
|
const newF = candidates[0]!;
|
|
181
213
|
const pf = priorFields[oldF]!;
|
|
182
|
-
fields[newF] = {
|
|
214
|
+
fields[newF] = { ...pf, was: [...(pf.was ?? []), oldF] };
|
|
183
215
|
inferred.push({ what: `${agg.id}.${newF}`, from: oldF, to: newF, sid: pf.sid });
|
|
184
216
|
}
|
|
185
217
|
}
|
package/src/stable_ids_types.ts
CHANGED
|
@@ -11,6 +11,22 @@
|
|
|
11
11
|
* (build-time only — `nomos-compile` + `@githolon/dsl/manifest`).
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
/** The read decoder needed to interpret one evolution rule's source value. */
|
|
15
|
+
export interface StableFieldValueKind {
|
|
16
|
+
readonly kind: string;
|
|
17
|
+
readonly mapValueKind?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** A deterministic source-contract → target-contract projection. `identity` is a
|
|
21
|
+
* compiler-proven widening; `cases` is an exhaustive finite business mapping. */
|
|
22
|
+
export interface StableFieldEvolution {
|
|
23
|
+
readonly from: string;
|
|
24
|
+
readonly to: string;
|
|
25
|
+
readonly source: StableFieldValueKind;
|
|
26
|
+
readonly mode: "identity" | "cases";
|
|
27
|
+
readonly cases?: readonly { readonly from: unknown; readonly to: unknown }[];
|
|
28
|
+
}
|
|
29
|
+
|
|
14
30
|
/** One field's stable identity: the minted sid + the labels it previously wore. */
|
|
15
31
|
export interface StableFieldId {
|
|
16
32
|
readonly sid: string;
|
|
@@ -21,6 +37,20 @@ export interface StableFieldId {
|
|
|
21
37
|
* Always set at emission; continuity input may omit it (the current module is the
|
|
22
38
|
* source of the CURRENT kind). */
|
|
23
39
|
readonly kind?: string;
|
|
40
|
+
/** Content identity of the COMPLETE generated business-value contract for this field
|
|
41
|
+
* (canonical Zod JSON schema plus the field metadata that shapes TS/Dart decoding).
|
|
42
|
+
* Conflict observations use it to distinguish an unrelated law rollout from an actual
|
|
43
|
+
* value-shape change; it is compiler-derived, never tenant-authored. */
|
|
44
|
+
readonly contract?: string;
|
|
45
|
+
/** Canonical descriptor hashed by `contract`. Retained so the next compiler can prove
|
|
46
|
+
* structural identity widening without asking the tenant to restate it. */
|
|
47
|
+
readonly shape?: unknown;
|
|
48
|
+
/** Complete stable schema identity recorded on conflict observations. */
|
|
49
|
+
readonly schema?: string;
|
|
50
|
+
/** Current kernel merge driver, retained for safe automatic evolution comparison. */
|
|
51
|
+
readonly driver?: unknown;
|
|
52
|
+
/** Every still-supported route, accumulated across releases. */
|
|
53
|
+
readonly evolutions?: readonly StableFieldEvolution[];
|
|
24
54
|
/** BIRTH-PRESENCE LAW (`.fromBirth()`): recorded at the field's first fromBirth emission.
|
|
25
55
|
* The compile guard refuses NEWLY declaring fromBirth on a sid that existed without it
|
|
26
56
|
* (old rows may lack the field — the non-nullable read type would lie). */
|
package/src/surfaced_lint.ts
CHANGED
|
Binary file
|
package/src/usd.ts
CHANGED
|
@@ -33,7 +33,11 @@ import {
|
|
|
33
33
|
type CanonicalManifest,
|
|
34
34
|
type CanonicalProjectionReturn,
|
|
35
35
|
} from "./manifest.js";
|
|
36
|
-
import
|
|
36
|
+
import {
|
|
37
|
+
OWNED_KEY_RELATION,
|
|
38
|
+
OWNED_READ_RELATION,
|
|
39
|
+
type AggregateHandle,
|
|
40
|
+
} from "./aggregate.js";
|
|
37
41
|
import {
|
|
38
42
|
mergeRequiresScope,
|
|
39
43
|
CompositionViolation,
|
|
@@ -54,6 +58,7 @@ import {
|
|
|
54
58
|
} from "./read_projection_namespace.js";
|
|
55
59
|
import { qualifyManifestAggregateLabels } from "./read_projection_manifest.js";
|
|
56
60
|
import { guardSharedReadProjectionKeys } from "./read_projection_composition.js";
|
|
61
|
+
import { OWNED_RECOVERY_RELATION } from "./framework/owned_custody.js";
|
|
57
62
|
|
|
58
63
|
/** The referential marker of a directive prim (kernel `RefMode` wire string). */
|
|
59
64
|
export type RefMode = WireRefMode;
|
|
@@ -552,7 +557,7 @@ export interface UsdLayer {
|
|
|
552
557
|
readonly nomosReadVisibility?: {
|
|
553
558
|
readonly v: 1;
|
|
554
559
|
/** Aggregate TYPE name → the capability a read touching it requires. SORTED by type name. */
|
|
555
|
-
readonly aggregates: Record<string, { requires: string }>;
|
|
560
|
+
readonly aggregates: Record<string, { requires: string; ownerField?: string }>;
|
|
556
561
|
};
|
|
557
562
|
/**
|
|
558
563
|
* THE ENCRYPTION GATE DECLARATION (e2e_encryption.md slice 2). Present IFF ≥1 field of
|
|
@@ -567,7 +572,7 @@ export interface UsdLayer {
|
|
|
567
572
|
*/
|
|
568
573
|
readonly nomosEncryption?: {
|
|
569
574
|
readonly v: 1;
|
|
570
|
-
readonly aggregates: Record<string, { scope: string; fields: string[]; delegatable: false }>;
|
|
575
|
+
readonly aggregates: Record<string, { scope: string; fields: string[]; delegatable: false; owned?: true; ownerField?: string }>;
|
|
571
576
|
};
|
|
572
577
|
/**
|
|
573
578
|
* THE CAPABILITY DECLARATION IN LAW (Full-A — capability_marketplace.md §4): the impure-
|
|
@@ -975,9 +980,9 @@ function synthesizeReadRelationTypes(
|
|
|
975
980
|
): import("./relation_schema.js").RelationTypeDecl[] {
|
|
976
981
|
const caps = new Set<string>();
|
|
977
982
|
for (const a of manifest.aggregates) {
|
|
978
|
-
if (a.requiresRead !== undefined) caps.add(a.requiresRead);
|
|
983
|
+
if (a.requiresRead !== undefined && a.owned === undefined) caps.add(a.requiresRead);
|
|
979
984
|
}
|
|
980
|
-
|
|
985
|
+
const out: import("./relation_schema.js").RelationTypeDecl[] = [];
|
|
981
986
|
// GLOBAL (un-namespaced): the read gate hardcodes `ObjectRef::new("workspace","self")` in Rust
|
|
982
987
|
// (`wasm_git_holon.rs`), so the synthesized read-visibility `workspace` type MUST stay bare — it is
|
|
983
988
|
// NOT namespaced by the owning domain (site-6 exemption; a Rust change would be needed to qualify it).
|
|
@@ -989,7 +994,26 @@ function synthesizeReadRelationTypes(
|
|
|
989
994
|
for (const cap of [...caps].sort()) {
|
|
990
995
|
builder.relation(cap, { allow: [user(), key(), anyUser()] });
|
|
991
996
|
}
|
|
992
|
-
|
|
997
|
+
if (caps.size > 0) out.push(builder);
|
|
998
|
+
// `.owned()` is record-grained: its read relation lives on the aggregate object itself, so the
|
|
999
|
+
// existing row filter checks `<qualified-type>:<record-id>#<relation>`. The type remains local and is
|
|
1000
|
+
// therefore qualified by the same domain key as the aggregate; only direct user principals may sit it.
|
|
1001
|
+
for (const a of [...manifest.aggregates].filter((x) => x.owned !== undefined).sort((x, y) => x.id.localeCompare(y.id))) {
|
|
1002
|
+
const owned = a.owned!;
|
|
1003
|
+
// `manifest` already carries the read-projection-qualified type (`domain.Note`). Keep that exact
|
|
1004
|
+
// spelling global here rather than applying the relation namespace (`domain__Note`): the read gate
|
|
1005
|
+
// constructs its per-record object from the projected row type, so schema, tuple and row must agree.
|
|
1006
|
+
out.push(relationType(a.id).asGlobal().relation(owned.relation, { allow: [user()] }));
|
|
1007
|
+
}
|
|
1008
|
+
if (manifest.aggregates.some((x) => x.owned !== undefined)) {
|
|
1009
|
+
out.push(
|
|
1010
|
+
relationType("nomos-owned-principal")
|
|
1011
|
+
.asGlobal()
|
|
1012
|
+
.relation(OWNED_KEY_RELATION, { allow: [key()] })
|
|
1013
|
+
.relation(OWNED_RECOVERY_RELATION, { allow: [user()] }),
|
|
1014
|
+
);
|
|
1015
|
+
}
|
|
1016
|
+
return out;
|
|
993
1017
|
}
|
|
994
1018
|
|
|
995
1019
|
/**
|
|
@@ -1021,6 +1045,21 @@ export function emitUsd(
|
|
|
1021
1045
|
const qaType = (n: string): string =>
|
|
1022
1046
|
namespacedAggs.has(n) ? qualifyReadProjectionType(rpDomainKey, n) : n;
|
|
1023
1047
|
const manifest = qualifyManifestAggregateLabels(bareManifest, rpDomainKey, namespacedAggs);
|
|
1048
|
+
// `.owned()` reserves its deliberately obscure relation spelling. If tenant law nevertheless reaches
|
|
1049
|
+
// for it, fail here instead of merging, qualifying or shadowing the declaration: a collision is useful
|
|
1050
|
+
// evidence that the tenant is modelling the same concept, but silently capturing it would make their
|
|
1051
|
+
// code mean something they did not write.
|
|
1052
|
+
const ownedRelations = new Set([OWNED_READ_RELATION, OWNED_KEY_RELATION, OWNED_RECOVERY_RELATION]);
|
|
1053
|
+
for (const d of l.module.relationTypes ?? []) {
|
|
1054
|
+
for (const relation of d.relations.keys()) {
|
|
1055
|
+
if (!ownedRelations.has(relation)) continue;
|
|
1056
|
+
throw new Error(
|
|
1057
|
+
`relationType(${JSON.stringify(d.type)}): relation ${JSON.stringify(relation)} is reserved by ` +
|
|
1058
|
+
`Nomos .owned(). Sorry mate, that concept is already taken by the framework; give the ` +
|
|
1059
|
+
`business relation a different name.`,
|
|
1060
|
+
);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1024
1063
|
// THE READ-PROJECTION NAMESPACE ERA KEY: present iff ≥1 aggregate was qualified (the witness
|
|
1025
1064
|
// marker — mirrors `nomosRelationNamespace`; the qualification itself is baked into the keys).
|
|
1026
1065
|
const nomosReadProjectionNamespace =
|
|
@@ -1053,7 +1092,9 @@ export function emitUsd(
|
|
|
1053
1092
|
// must be qualified. A `global` type (governance `workspace`, signer `identity`, read-visibility
|
|
1054
1093
|
// `workspace`) stays bare on BOTH the schema and the gate side, so it is excluded here.
|
|
1055
1094
|
const namespacedTypes = new Set(
|
|
1056
|
-
(l.module.relationTypes ?? [])
|
|
1095
|
+
[...(l.module.relationTypes ?? []), ...readRelationTypes]
|
|
1096
|
+
.filter((d) => d.global !== true)
|
|
1097
|
+
.map((d) => d.type),
|
|
1057
1098
|
);
|
|
1058
1099
|
// THE AMBIGUITY GUARD (relation-namespacing): a bare `writeTuple({object:"workspace:self", relation:R})`
|
|
1059
1100
|
// is auto-qualified iff `(type,R)` is a LOCAL non-global authored relation, and left BARE iff it is a
|
|
@@ -1388,20 +1429,30 @@ export function emitUsd(
|
|
|
1388
1429
|
// THE READ-VISIBILITY GATE (slice 5.1): every aggregate the manifest carries a
|
|
1389
1430
|
// `requiresRead` for, keyed by TYPE name → required cap. SORTED keys
|
|
1390
1431
|
// (byte-determinism); omit-when-empty so an all-public layer is byte-identical.
|
|
1391
|
-
const visAggregates: Record<string, { requires: string }> = {};
|
|
1392
|
-
const encAggregates: Record<string, { scope: string; fields: string[]; delegatable: false }> = {};
|
|
1432
|
+
const visAggregates: Record<string, { requires: string; ownerField?: string }> = {};
|
|
1433
|
+
const encAggregates: Record<string, { scope: string; fields: string[]; delegatable: false; owned?: true; ownerField?: string }> = {};
|
|
1393
1434
|
// ATTRIBUTED READS (field_provenance.md): aggregate TYPE → its attributed field
|
|
1394
1435
|
// names — the self-describing nomosAttributedGate era key. Omit-when-empty.
|
|
1395
1436
|
const attrAggregates: Record<string, string[]> = {};
|
|
1396
1437
|
for (const a of [...manifest.aggregates].sort((x, y) => (x.id < y.id ? -1 : x.id > y.id ? 1 : 0))) {
|
|
1397
|
-
if (a.requiresRead !== undefined)
|
|
1438
|
+
if (a.requiresRead !== undefined) {
|
|
1439
|
+
visAggregates[a.id] = {
|
|
1440
|
+
requires: a.requiresRead,
|
|
1441
|
+
...(a.owned !== undefined ? { ownerField: a.owned.ownerField } : {}),
|
|
1442
|
+
};
|
|
1443
|
+
}
|
|
1398
1444
|
if (a.attributedFields !== undefined && a.attributedFields.length > 0) {
|
|
1399
1445
|
attrAggregates[a.id] = [...a.attributedFields];
|
|
1400
1446
|
}
|
|
1401
1447
|
// THE ENCRYPTION GATE (slice 2): an aggregate with encrypted field(s) — its scope
|
|
1402
1448
|
// is its read cap (aggregate()/the gate guarantee they coincide). delegatable:false.
|
|
1403
1449
|
if (a.encryptedFields !== undefined && a.encryptedFields.length > 0 && a.requiresRead !== undefined) {
|
|
1404
|
-
encAggregates[a.id] = {
|
|
1450
|
+
encAggregates[a.id] = {
|
|
1451
|
+
scope: a.requiresRead,
|
|
1452
|
+
fields: [...a.encryptedFields],
|
|
1453
|
+
delegatable: false,
|
|
1454
|
+
...(a.owned !== undefined ? { owned: true as const, ownerField: a.owned.ownerField } : {}),
|
|
1455
|
+
};
|
|
1405
1456
|
}
|
|
1406
1457
|
}
|
|
1407
1458
|
// THE CAPABILITY DECLARATION IN LAW (Full-A — capability_marketplace.md §4): the
|
package/src/usd_layers.ts
CHANGED
|
@@ -72,6 +72,7 @@ import {
|
|
|
72
72
|
type UsdQuery,
|
|
73
73
|
} from "./usd.js";
|
|
74
74
|
import { composeSharedRelationTypes } from "./relation_composition.js";
|
|
75
|
+
import { withBusinessCorrectionLayer } from "./framework/business_corrections.js";
|
|
75
76
|
|
|
76
77
|
/** The layer-file format marker (customLayerData `nomos:format`). */
|
|
77
78
|
export const NOMOS_USD_LAYER_FORMAT = "nomos.usd-layer.v1";
|
|
@@ -228,6 +229,9 @@ function domainLayerAttrLines(domain: string, layer: UsdLayer, indent: string):
|
|
|
228
229
|
...(layer.nomosForeignRefs !== undefined
|
|
229
230
|
? [`${indent}custom string nomos:foreignRefsHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosForeignRefs)))}`]
|
|
230
231
|
: []),
|
|
232
|
+
...(layer.nomosFlowGate !== undefined
|
|
233
|
+
? [`${indent}custom string nomos:flowGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosFlowGate)))}`]
|
|
234
|
+
: []),
|
|
231
235
|
];
|
|
232
236
|
}
|
|
233
237
|
|
|
@@ -265,10 +269,12 @@ export function emitLayeredDomain(
|
|
|
265
269
|
if (modules.length === 0) {
|
|
266
270
|
throw new Error(`usd-layers: domain '${domain}' has no modules to layer`);
|
|
267
271
|
}
|
|
272
|
+
const normalizedModules = withBusinessCorrectionLayer(modules.map((entry) => entry.module));
|
|
268
273
|
const out: EmittedModuleLayer[] = [];
|
|
269
274
|
const seenPaths = new Set<string>();
|
|
270
|
-
for (const m of modules) {
|
|
271
|
-
const
|
|
275
|
+
for (const [index, m] of modules.entries()) {
|
|
276
|
+
const module = normalizedModules[index]!;
|
|
277
|
+
const doc = emitUsd([{ path: `/Nomos/${domain}`, module }]);
|
|
272
278
|
const layer = doc.layers[0]!;
|
|
273
279
|
const overPaths = new Set<string>();
|
|
274
280
|
for (const prim of layer.prims) {
|
|
@@ -277,7 +283,7 @@ export function emitLayeredDomain(
|
|
|
277
283
|
out.push({
|
|
278
284
|
name: m.name,
|
|
279
285
|
layer,
|
|
280
|
-
usda: usdaLayerText(domain, m.name, layer,
|
|
286
|
+
usda: usdaLayerText(domain, m.name, layer, module, overPaths),
|
|
281
287
|
});
|
|
282
288
|
for (const prim of layer.prims) seenPaths.add(prim.path);
|
|
283
289
|
}
|
|
@@ -1031,6 +1037,43 @@ export function flattenLayeredUsd(layers: readonly UsdLayer[]): UsdDocument {
|
|
|
1031
1037
|
};
|
|
1032
1038
|
}
|
|
1033
1039
|
}
|
|
1040
|
+
// THE FLOW GATE: a stronger directive replaces the weaker declaration wholesale, so its
|
|
1041
|
+
// harvested endorsement flows must do the same (including replacing them with none). Other
|
|
1042
|
+
// flow rows union by their stable site. Both halves retain emitUsd's canonical sort order.
|
|
1043
|
+
let flowGate: UsdLayer["nomosFlowGate"];
|
|
1044
|
+
{
|
|
1045
|
+
type Endorse = NonNullable<NonNullable<UsdLayer["nomosFlowGate"]>["endorse"]>[number];
|
|
1046
|
+
type Declassify = NonNullable<NonNullable<UsdLayer["nomosFlowGate"]>["declassify"]>[number];
|
|
1047
|
+
const endorseByDirective = new Map<string, Endorse[]>();
|
|
1048
|
+
const declassifyBySite = new Map<string, Declassify>();
|
|
1049
|
+
for (const layer of stack) {
|
|
1050
|
+
const touchedDirectives = new Set(
|
|
1051
|
+
layer.nomosIdentityManifest?.directives.map((directive) => directive.id) ?? [],
|
|
1052
|
+
);
|
|
1053
|
+
for (const directiveId of touchedDirectives) endorseByDirective.delete(directiveId);
|
|
1054
|
+
for (const row of layer.nomosFlowGate?.endorse ?? []) {
|
|
1055
|
+
const rows = endorseByDirective.get(row.directiveId) ?? [];
|
|
1056
|
+
rows.push(row);
|
|
1057
|
+
endorseByDirective.set(row.directiveId, rows);
|
|
1058
|
+
}
|
|
1059
|
+
for (const row of layer.nomosFlowGate?.declassify ?? []) declassifyBySite.set(row.site, row);
|
|
1060
|
+
}
|
|
1061
|
+
const declassify = [...declassifyBySite.values()].sort((a, b) =>
|
|
1062
|
+
a.site < b.site ? -1 : a.site > b.site ? 1 : 0,
|
|
1063
|
+
);
|
|
1064
|
+
const endorse = [...endorseByDirective.values()].flat().sort((a, b) =>
|
|
1065
|
+
a.directiveId !== b.directiveId ? (a.directiveId < b.directiveId ? -1 : 1)
|
|
1066
|
+
: a.sink !== b.sink ? (a.sink < b.sink ? -1 : 1)
|
|
1067
|
+
: a.facet < b.facet ? -1 : a.facet > b.facet ? 1 : 0,
|
|
1068
|
+
);
|
|
1069
|
+
if (declassify.length > 0 || endorse.length > 0) {
|
|
1070
|
+
flowGate = {
|
|
1071
|
+
v: 1,
|
|
1072
|
+
...(declassify.length > 0 ? { declassify } : {}),
|
|
1073
|
+
...(endorse.length > 0 ? { endorse } : {}),
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1034
1077
|
return {
|
|
1035
1078
|
path,
|
|
1036
1079
|
prims: [...prims.keys()].sort().map((p) => prims.get(p)!),
|
|
@@ -1060,6 +1103,7 @@ return {
|
|
|
1060
1103
|
...(valueObjects !== undefined ? { nomosValueObjects: valueObjects } : {}),
|
|
1061
1104
|
...(foreignRefs !== undefined ? { nomosForeignRefs: foreignRefs } : {}),
|
|
1062
1105
|
...(contracts !== undefined ? { nomosContracts: contracts } : {}),
|
|
1106
|
+
...(flowGate !== undefined ? { nomosFlowGate: flowGate } : {}),
|
|
1063
1107
|
};
|
|
1064
1108
|
});
|
|
1065
1109
|
|
|
@@ -1203,6 +1247,7 @@ export function parseUsdaDocument(text: string): UsdDocument {
|
|
|
1203
1247
|
const valueObjectsByDomain = new Map<string, UsdLayer["nomosValueObjects"]>();
|
|
1204
1248
|
const foreignRefsByDomain = new Map<string, UsdLayer["nomosForeignRefs"]>();
|
|
1205
1249
|
const contractsByDomain = new Map<string, UsdLayer["nomosContracts"]>();
|
|
1250
|
+
const flowGateByDomain = new Map<string, UsdLayer["nomosFlowGate"]>();
|
|
1206
1251
|
for (const p of prims) {
|
|
1207
1252
|
if (p.attrs.get("nomos:kind") === "domain") {
|
|
1208
1253
|
const domain = attrString(p, "nomos:domain");
|
|
@@ -1317,6 +1362,10 @@ export function parseUsdaDocument(text: string): UsdDocument {
|
|
|
1317
1362
|
if (typeof ctHex === "string") {
|
|
1318
1363
|
contractsByDomain.set(domain, JSON.parse(unhexUtf8(ctHex)) as UsdLayer["nomosContracts"]);
|
|
1319
1364
|
}
|
|
1365
|
+
const flowGateHex = p.attrs.get("nomos:flowGateHex");
|
|
1366
|
+
if (typeof flowGateHex === "string") {
|
|
1367
|
+
flowGateByDomain.set(domain, JSON.parse(unhexUtf8(flowGateHex)) as UsdLayer["nomosFlowGate"]);
|
|
1368
|
+
}
|
|
1320
1369
|
}
|
|
1321
1370
|
}
|
|
1322
1371
|
|
|
@@ -1431,6 +1480,7 @@ export function parseUsdaDocument(text: string): UsdDocument {
|
|
|
1431
1480
|
const valueObjects = valueObjectsByDomain.get(domain);
|
|
1432
1481
|
const foreignRefs2 = foreignRefsByDomain.get(domain);
|
|
1433
1482
|
const contracts2 = contractsByDomain.get(domain);
|
|
1483
|
+
const flowGate2 = flowGateByDomain.get(domain);
|
|
1434
1484
|
return {
|
|
1435
1485
|
path: `/Nomos/${domain}`,
|
|
1436
1486
|
prims: [...b.prims].sort(byPath),
|
|
@@ -1460,6 +1510,7 @@ export function parseUsdaDocument(text: string): UsdDocument {
|
|
|
1460
1510
|
...(valueObjects !== undefined ? { nomosValueObjects: valueObjects } : {}),
|
|
1461
1511
|
...(foreignRefs2 !== undefined ? { nomosForeignRefs: foreignRefs2 } : {}),
|
|
1462
1512
|
...(contracts2 !== undefined ? { nomosContracts: contracts2 } : {}),
|
|
1513
|
+
...(flowGate2 !== undefined ? { nomosFlowGate: flowGate2 } : {}),
|
|
1463
1514
|
};
|
|
1464
1515
|
})
|
|
1465
1516
|
.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
|
package/src/value_object.ts
CHANGED
|
@@ -39,9 +39,10 @@
|
|
|
39
39
|
* value. When subfields must merge independently, model them as aggregate fields or a
|
|
40
40
|
* `t.map(...)` — that is what aggregates/maps are for.
|
|
41
41
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
42
|
+
* SCHEMA IDENTITY: the VO metadata never changes the driver-only kernel wire schema, but
|
|
43
|
+
* its canonical descriptor is included in the stable field value contract. A VO shape
|
|
44
|
+
* change therefore changes field/law identity so a gradual rollout cannot cast an old
|
|
45
|
+
* observation as the new type. The `nomosValueObjects` USD key is emitted IFF a domain's
|
|
45
46
|
* aggregates carry ≥1 VO field; a VO-free domain's package bytes are untouched.
|
|
46
47
|
*
|
|
47
48
|
* THE CONSTRAINED FIELD SET (no fallback — anything else throws at compile):
|
|
@@ -228,9 +229,10 @@ function zodNumberIsInt(zt: z.ZodTypeAny): boolean {
|
|
|
228
229
|
/**
|
|
229
230
|
* THE UNIT BRAND (dart_codegen_parity.md P2) — `unit("Millimetres", z.number())` stamps the
|
|
230
231
|
* schema so the descriptor carries the unit name and the generated Dart wraps the field in a
|
|
231
|
-
* ZERO-COST extension type (`Millimetres`).
|
|
232
|
-
*
|
|
233
|
-
*
|
|
232
|
+
* ZERO-COST extension type (`Millimetres`). The wire value stays a plain JSON number and
|
|
233
|
+
* validation is untouched. When the unit appears in an aggregate value-object descriptor it
|
|
234
|
+
* is part of that field's stable value contract; payload-only use remains codegen metadata.
|
|
235
|
+
* v1 = opaque tags — NO generated arithmetic or conversions (a physics library is not a type system).
|
|
234
236
|
*/
|
|
235
237
|
export const NOMOS_UNIT_BRAND: unique symbol = Symbol.for("nomos.unit");
|
|
236
238
|
|
|
@@ -253,7 +255,8 @@ export function unit<Z extends z.ZodTypeAny>(name: string, inner: Z): Z {
|
|
|
253
255
|
* wire-identical to `z.string()` but the generated Dart field is the aggregate's TYPED id
|
|
254
256
|
* class (`SiteId`), and the generated TS references the branded alias. Passing another
|
|
255
257
|
* aggregate's id where a SiteId belongs becomes a COMPILE error. Payload-side only — the
|
|
256
|
-
* kernel keeps seeing a string; validation
|
|
258
|
+
* kernel keeps seeing a string; validation and the aggregate field contract are untouched
|
|
259
|
+
* because this is a directive-payload annotation.
|
|
257
260
|
*/
|
|
258
261
|
export const NOMOS_IDOF_BRAND: unique symbol = Symbol.for("nomos.idOf");
|
|
259
262
|
|
|
@@ -270,8 +273,7 @@ export function idOf(aggregate: { id: string } | string): z.ZodType<string> {
|
|
|
270
273
|
|
|
271
274
|
/**
|
|
272
275
|
* THE BRANDED-ID BRAND (dart_codegen_parity.md P2 — the standalone twin of {@link idOf}) —
|
|
273
|
-
* `brandedId("SiteId")` is a NOMINAL string: wire-identical to `z.string()`
|
|
274
|
-
* domainHash untouched) but the generated TS is a branded alias
|
|
276
|
+
* `brandedId("SiteId")` is a NOMINAL string: wire-identical to `z.string()` but the generated TS is a branded alias
|
|
275
277
|
* (`type SiteId = string & { readonly __brand }`) and the generated Dart a zero-cost
|
|
276
278
|
* extension type (`extension type const SiteId(String value) {}`), so passing a bare string
|
|
277
279
|
* — or another brand — where a `SiteId` belongs is a COMPILE error. Unlike `idOf`, it is NOT
|
|
@@ -430,9 +432,9 @@ const VO_NAME_RE = /^[A-Za-z][A-Za-z0-9_]*$/;
|
|
|
430
432
|
|
|
431
433
|
/**
|
|
432
434
|
* THE VO BRAND (slice A of vo_authoring_end_to_end.md) — `valueObject()` stamps its zod schema
|
|
433
|
-
* with the VO's {@link ValueObjectFieldMeta} under this symbol
|
|
434
|
-
*
|
|
435
|
-
*
|
|
435
|
+
* with the VO's {@link ValueObjectFieldMeta} under this symbol. The property itself is
|
|
436
|
+
* non-enumerable, never serialized and never read by the engine's validation path. When attached
|
|
437
|
+
* to an aggregate field, its canonical descriptor deliberately enters that field's stable value contract.
|
|
436
438
|
* Every generated-surface introspector (Dart/TS payload gen, interface kinds, proof synthesis)
|
|
437
439
|
* consults {@link voMetaOf} BEFORE its raw zod-kind switch — the VO keeps its NAME on every
|
|
438
440
|
* surface instead of degrading to an anonymous json object.
|
|
@@ -565,8 +567,9 @@ function makeValueObject<T>(
|
|
|
565
567
|
...(geometry !== undefined ? { geometry } : {}),
|
|
566
568
|
...(example !== undefined ? { example } : {}),
|
|
567
569
|
};
|
|
568
|
-
// THE BRAND (slice A): compile-time identity on the schema itself — non-enumerable
|
|
569
|
-
// serialized
|
|
570
|
+
// THE BRAND (slice A): compile-time identity on the schema itself — non-enumerable and never
|
|
571
|
+
// serialized directly. voMetaOf() is the one resolver every introspector uses; aggregate
|
|
572
|
+
// fields separately copy the canonical descriptor into their stable value contract.
|
|
570
573
|
Object.defineProperty(zodSchema, NOMOS_VO_BRAND, { value: meta, enumerable: false });
|
|
571
574
|
const vo: ValueObject<T> = {
|
|
572
575
|
__isValueObject: true,
|
|
@@ -583,10 +586,10 @@ function makeValueObject<T>(
|
|
|
583
586
|
},
|
|
584
587
|
merge(driver: Driver): Field<T, "json"> {
|
|
585
588
|
const k = driver.kind;
|
|
586
|
-
if (k !== "Lww" && k !== "LwwCausal" && k !== "LwwBy") {
|
|
589
|
+
if (k !== "FirstWriterWins" && k !== "Lww" && k !== "LwwCausal" && k !== "LwwBy") {
|
|
587
590
|
throw new Error(
|
|
588
591
|
`valueObject '${name}': .merge(${k}) is not a valid value-object field merge — a VO is ONE ` +
|
|
589
|
-
`
|
|
592
|
+
`wholesale json leaf, so only FirstWriterWins / Lww / LwwCausal / LwwBy apply. Field-level ` +
|
|
590
593
|
`merge composition of a VO's subfields is a later slice; model independently-merging subfields ` +
|
|
591
594
|
`as aggregate fields or a t.map(...).`,
|
|
592
595
|
);
|
|
@@ -597,7 +600,7 @@ function makeValueObject<T>(
|
|
|
597
600
|
// THE HANDLE IS A FIRST-CLASS ZOD SCHEMA (vo_first_class.md slice 1): delegate zod's
|
|
598
601
|
// internals so the raw HANDLE composes DIRECTLY where a zod is expected — `z.array(Vo)`,
|
|
599
602
|
// `z.object({ f: Vo })`, a directive payload — with NO `.zod` peeling. Non-enumerable (never
|
|
600
|
-
// serialized
|
|
603
|
+
// serialized directly) and stamped with the VO brand so describeZod / voMetaOf resolve
|
|
601
604
|
// the handle exactly like its `.zod`.
|
|
602
605
|
const inner = zodSchema as unknown as { _zod?: unknown; _def?: unknown; def?: unknown };
|
|
603
606
|
Object.defineProperty(vo, "_zod", { get: () => inner._zod, enumerable: false });
|
|
@@ -918,8 +921,9 @@ export function collectValueObjectDescriptors(
|
|
|
918
921
|
* ONE VO↔FIELD ATTACHMENT (the IR pointer): aggregate `aggregate`'s field `field`
|
|
919
922
|
* stores value object `vo`. Rides the additive `nomosValueObjects` USD key as
|
|
920
923
|
* `usages` so tooling (the scene projector, the explorer glyphs) can attach a VO
|
|
921
|
-
* molecule to the exact field that carries it.
|
|
922
|
-
* manifest
|
|
924
|
+
* molecule to the exact field that carries it. The pointer itself is not in the canonical
|
|
925
|
+
* manifest; the VO descriptor is already hash-bearing through the stable field value contract,
|
|
926
|
+
* while the kernel wire schema stays a plain `jsonObject` leaf.
|
|
923
927
|
*/
|
|
924
928
|
export interface ValueObjectUsage {
|
|
925
929
|
readonly aggregate: string;
|
package/src/wire.ts
CHANGED
package/src/wire_encode.ts
CHANGED
|
@@ -30,7 +30,12 @@ import {
|
|
|
30
30
|
captureBirthMeterReads,
|
|
31
31
|
planBirthRecordOps,
|
|
32
32
|
} from "./birth_meter.js";
|
|
33
|
-
import
|
|
33
|
+
import {
|
|
34
|
+
OWNED_CUSTODY_PAYLOAD_FIELD,
|
|
35
|
+
OWNED_KEY_PAYLOAD_FIELD,
|
|
36
|
+
OWNED_PREMINTED_RECORDS_PAYLOAD_FIELD,
|
|
37
|
+
type AggregateHandle,
|
|
38
|
+
} from "./aggregate.js";
|
|
34
39
|
import { admitsNull, type Field, type EvidenceRefValue } from "./fields.js";
|
|
35
40
|
import type { Directive, ReferentialMarker } from "./directive.js";
|
|
36
41
|
import type { PlannedOp, FieldOp } from "./ops.js";
|
|
@@ -282,7 +287,40 @@ export function executeDirectiveToIntent<P>(
|
|
|
282
287
|
`but was given handle '${agg.id}'.`,
|
|
283
288
|
);
|
|
284
289
|
}
|
|
285
|
-
const
|
|
290
|
+
const rawPayload = payload as unknown;
|
|
291
|
+
const ownedKeySubject = typeof rawPayload === "object" && rawPayload !== null
|
|
292
|
+
? (rawPayload as Record<string, unknown>)[OWNED_KEY_PAYLOAD_FIELD]
|
|
293
|
+
: undefined;
|
|
294
|
+
const ownedCustody = typeof rawPayload === "object" && rawPayload !== null
|
|
295
|
+
? (rawPayload as Record<string, unknown>)[OWNED_CUSTODY_PAYLOAD_FIELD]
|
|
296
|
+
: undefined;
|
|
297
|
+
const preMintedRecordsRaw = typeof rawPayload === "object" && rawPayload !== null
|
|
298
|
+
? (rawPayload as Record<string, unknown>)[OWNED_PREMINTED_RECORDS_PAYLOAD_FIELD]
|
|
299
|
+
: undefined;
|
|
300
|
+
if (preMintedRecordsRaw !== undefined && !Array.isArray(preMintedRecordsRaw)) {
|
|
301
|
+
throw new Error("Nomos owned pre-minted record identities must be an array");
|
|
302
|
+
}
|
|
303
|
+
const preMintedRecords = Array.isArray(preMintedRecordsRaw)
|
|
304
|
+
? preMintedRecordsRaw.map((value) => {
|
|
305
|
+
if (typeof value !== "object" || value === null) {
|
|
306
|
+
throw new Error("Nomos owned pre-minted record identity is malformed");
|
|
307
|
+
}
|
|
308
|
+
const type = (value as Record<string, unknown>).type;
|
|
309
|
+
const id = (value as Record<string, unknown>).id;
|
|
310
|
+
if (typeof type !== "string" || type.length === 0 || typeof id !== "string" || id.length === 0) {
|
|
311
|
+
throw new Error("Nomos owned pre-minted record identity must carry non-empty type and id strings");
|
|
312
|
+
}
|
|
313
|
+
return { type, id };
|
|
314
|
+
})
|
|
315
|
+
: [];
|
|
316
|
+
const businessPayload = typeof rawPayload === "object" && rawPayload !== null
|
|
317
|
+
? Object.fromEntries(Object.entries(rawPayload as Record<string, unknown>).filter(
|
|
318
|
+
([key]) => key !== OWNED_KEY_PAYLOAD_FIELD
|
|
319
|
+
&& key !== OWNED_CUSTODY_PAYLOAD_FIELD
|
|
320
|
+
&& key !== OWNED_PREMINTED_RECORDS_PAYLOAD_FIELD,
|
|
321
|
+
))
|
|
322
|
+
: rawPayload;
|
|
323
|
+
const parsed = directive.payloadSchema.parse(businessPayload);
|
|
286
324
|
const hlc = ctx.clock();
|
|
287
325
|
const marker = encodeRefMode(directive.marker);
|
|
288
326
|
// THE NOMOS SHAPE: a plan emits events across aggregates via TWO library doors, merged here —
|
|
@@ -290,7 +328,13 @@ export function executeDirectiveToIntent<P>(
|
|
|
290
328
|
// (2) the plan may ALSO return explicit `PlannedOp`s (the typed `set`/`addToSet`/`strike` surface).
|
|
291
329
|
// Reset the sinks, run the plan, then merge authored ops (births/relations) ahead of the returned ops.
|
|
292
330
|
// The plan may also READ (O(1) DSL queries via `read(...)`) — captured into the footprint below.
|
|
293
|
-
__resetAuthoring(
|
|
331
|
+
__resetAuthoring(
|
|
332
|
+
ctx,
|
|
333
|
+
String(hlc.physical),
|
|
334
|
+
typeof ownedKeySubject === "string" ? ownedKeySubject : undefined,
|
|
335
|
+
typeof ownedCustody === "string" ? ownedCustody : null,
|
|
336
|
+
preMintedRecords,
|
|
337
|
+
);
|
|
294
338
|
__resetReads();
|
|
295
339
|
__resetBirths();
|
|
296
340
|
const returned = directive.plan(parsed, ctx);
|
|
@@ -482,7 +526,7 @@ export function harvestPrincipalFlows(directive: Directive<unknown>): PrincipalF
|
|
|
482
526
|
g.nomos = { ...(prior ?? {}), mint: (typeTag: string) => `${typeTag}_0192f00d-0000-7000-8000-000000000000` };
|
|
483
527
|
}
|
|
484
528
|
let ops: FieldOp[];
|
|
485
|
-
__resetAuthoring();
|
|
529
|
+
__resetAuthoring(ctx, String(ctx.clock().physical));
|
|
486
530
|
__resetReads();
|
|
487
531
|
try {
|
|
488
532
|
const returned = directive.plan(probe as never, ctx);
|