@githolon/dsl 0.33.0 → 0.34.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/aggregate.ts +12 -1
- package/src/authoring.ts +4 -1
- package/src/birth_meter.ts +300 -0
- package/src/birth_meter_ensure.ts +67 -0
- package/src/build_package.ts +34 -0
- package/src/codegen_dart.ts +9 -5
- package/src/codegen_proof.ts +13 -7
- package/src/compile_engine.ts +13 -2
- package/src/compile_package_main.ts +28 -1
- package/src/directive.ts +10 -1
- package/src/engine_entry.ts +56 -9
- package/src/framework/birth_meter.ts +125 -0
- package/src/framework/impure_capability.ts +5 -2
- package/src/framework/platform_admin.ts +4 -1
- package/src/framework/relations.ts +19 -0
- package/src/framework/workspaces.ts +50 -4
- package/src/index.ts +34 -0
- package/src/read.ts +22 -0
- package/src/read_projection_composition.ts +117 -0
- package/src/read_projection_manifest.ts +94 -0
- package/src/read_projection_namespace.ts +152 -0
- package/src/usd.ts +121 -11
- package/src/usd_layers.ts +71 -0
- package/src/value_object.ts +43 -0
- package/src/wire_encode.ts +34 -3
package/src/usd_layers.ts
CHANGED
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
* the runtime barrel.
|
|
59
59
|
*/
|
|
60
60
|
import type { AggregateHandle } from "./aggregate.js";
|
|
61
|
+
import { READ_PROJECTION_DELIMITER } from "./read_projection_namespace.js";
|
|
61
62
|
import type { Field } from "./fields.js";
|
|
62
63
|
import type { DomainModule } from "./codegen_dart.js";
|
|
63
64
|
import type { WireDriver, WireRefMode, WireSchema } from "./wire.js";
|
|
@@ -175,6 +176,9 @@ function domainLayerAttrLines(domain: string, layer: UsdLayer, indent: string):
|
|
|
175
176
|
...(layer.nomosRelationNamespace !== undefined
|
|
176
177
|
? [`${indent}custom string nomos:relationNamespaceHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosRelationNamespace)))}`]
|
|
177
178
|
: []),
|
|
179
|
+
...(layer.nomosReadProjectionNamespace !== undefined
|
|
180
|
+
? [`${indent}custom string nomos:readProjectionNamespaceHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosReadProjectionNamespace)))}`]
|
|
181
|
+
: []),
|
|
178
182
|
...(layer.nomosRelationGate !== undefined
|
|
179
183
|
? [`${indent}custom string nomos:relationGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosRelationGate)))}`]
|
|
180
184
|
: []),
|
|
@@ -184,6 +188,9 @@ function domainLayerAttrLines(domain: string, layer: UsdLayer, indent: string):
|
|
|
184
188
|
...(layer.nomosBirthGate !== undefined
|
|
185
189
|
? [`${indent}custom string nomos:birthGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosBirthGate)))}`]
|
|
186
190
|
: []),
|
|
191
|
+
...(layer.nomosBirthRecordGate !== undefined
|
|
192
|
+
? [`${indent}custom string nomos:birthRecordGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosBirthRecordGate)))}`]
|
|
193
|
+
: []),
|
|
187
194
|
...(layer.nomosParentAttestedBirth !== undefined
|
|
188
195
|
? [`${indent}custom string nomos:parentAttestedBirthHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosParentAttestedBirth)))}`]
|
|
189
196
|
: []),
|
|
@@ -779,6 +786,18 @@ export function flattenLayeredUsd(layers: readonly UsdLayer[]): UsdDocument {
|
|
|
779
786
|
}
|
|
780
787
|
if (ns.size > 0) relationNamespace = { v: 1, delimiter: "__", types: [...ns].sort() };
|
|
781
788
|
}
|
|
789
|
+
// THE READ-PROJECTION NAMESPACE ERA KEY (read-projection namespacing): the SORTED union of every
|
|
790
|
+
// layer's qualified aggregate type names. Present iff ANY layer namespaced >=1 aggregate;
|
|
791
|
+
// omit-when-empty (byte-identical for global-only packages). Delimiter is the ratified "__".
|
|
792
|
+
let readProjectionNamespace: UsdLayer["nomosReadProjectionNamespace"];
|
|
793
|
+
{
|
|
794
|
+
const ns = new Set<string>();
|
|
795
|
+
for (const l of stack) {
|
|
796
|
+
if (l.nomosReadProjectionNamespace === undefined) continue;
|
|
797
|
+
for (const id of l.nomosReadProjectionNamespace.types) ns.add(id);
|
|
798
|
+
}
|
|
799
|
+
if (ns.size > 0) readProjectionNamespace = { v: 1, delimiter: READ_PROJECTION_DELIMITER, types: [...ns].sort() };
|
|
800
|
+
}
|
|
782
801
|
// THE RELATION GATE (P4): directiveId → {relation, object} merged later-wins, SORTED. Omit-when-empty.
|
|
783
802
|
let relationGate: UsdLayer["nomosRelationGate"];
|
|
784
803
|
{
|
|
@@ -820,6 +839,16 @@ export function flattenLayeredUsd(layers: readonly UsdLayer[]): UsdDocument {
|
|
|
820
839
|
}
|
|
821
840
|
if (b.size > 0) birthGate = { v: 1, directives: [...b].sort() };
|
|
822
841
|
}
|
|
842
|
+
// THE BIRTH-RECORD GATE (metered birth, A1): present iff ANY layer declares it (once declared,
|
|
843
|
+
// declared). Carry the FIRST layer's aggregate/prefix verbatim (the framework constants — do NOT
|
|
844
|
+
// reconstruct). Omit-when-absent (era rule).
|
|
845
|
+
let birthRecordGate: UsdLayer["nomosBirthRecordGate"];
|
|
846
|
+
for (const l of stack) {
|
|
847
|
+
if (l.nomosBirthRecordGate !== undefined) {
|
|
848
|
+
birthRecordGate = l.nomosBirthRecordGate;
|
|
849
|
+
break;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
823
852
|
// THE PEER-SOVEREIGN ROOT-OF-TRUST: the era key is present iff ANY layer declares it (once required,
|
|
824
853
|
// required). Carry the FIRST layer's declared owner/cert locators verbatim (preserve them — do NOT
|
|
825
854
|
// reconstruct {v:1}, which would drop the locators the kernel reads). Omit-when-absent (era rule).
|
|
@@ -846,6 +875,29 @@ export function flattenLayeredUsd(layers: readonly UsdLayer[]): UsdDocument {
|
|
|
846
875
|
verifiedFirstEnroll = { v: 1, directives: rec };
|
|
847
876
|
}
|
|
848
877
|
}
|
|
878
|
+
// FIRST-CLASS VALUE OBJECTS (+ the VO↔field usage pointers): descriptors merged
|
|
879
|
+
// later-wins per NAME, usages later-wins per (aggregate, field), both SORTED —
|
|
880
|
+
// so the layered fold stays byte-homomorphic with `emitUsd`'s composed emission.
|
|
881
|
+
// Omit-when-empty (the era rule); `usages` omitted when none (0.33.0 tolerance).
|
|
882
|
+
let valueObjects: UsdLayer["nomosValueObjects"];
|
|
883
|
+
{
|
|
884
|
+
type VoDesc = NonNullable<UsdLayer["nomosValueObjects"]>["valueObjects"][number];
|
|
885
|
+
type VoUse = NonNullable<NonNullable<UsdLayer["nomosValueObjects"]>["usages"]>[number];
|
|
886
|
+
const byName = new Map<string, VoDesc>();
|
|
887
|
+
const byField = new Map<string, VoUse>();
|
|
888
|
+
for (const l of stack) {
|
|
889
|
+
if (l.nomosValueObjects === undefined) continue;
|
|
890
|
+
for (const d of l.nomosValueObjects.valueObjects) byName.set(d.name, d);
|
|
891
|
+
for (const u of l.nomosValueObjects.usages ?? []) byField.set(`${u.aggregate}\u0000${u.field}`, u);
|
|
892
|
+
}
|
|
893
|
+
if (byName.size > 0) {
|
|
894
|
+
const descs = [...byName.values()].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
895
|
+
const uses = [...byField.values()].sort((a, b) =>
|
|
896
|
+
a.aggregate !== b.aggregate ? (a.aggregate < b.aggregate ? -1 : 1) : a.field < b.field ? -1 : a.field > b.field ? 1 : 0,
|
|
897
|
+
);
|
|
898
|
+
valueObjects = { v: 1, valueObjects: descs, ...(uses.length > 0 ? { usages: uses } : {}) };
|
|
899
|
+
}
|
|
900
|
+
}
|
|
849
901
|
return {
|
|
850
902
|
path,
|
|
851
903
|
prims: [...prims.keys()].sort().map((p) => prims.get(p)!),
|
|
@@ -861,11 +913,14 @@ export function flattenLayeredUsd(layers: readonly UsdLayer[]): UsdDocument {
|
|
|
861
913
|
...(encryption !== undefined ? { nomosEncryption: encryption } : {}),
|
|
862
914
|
...(relations !== undefined ? { nomosRelations: relations } : {}),
|
|
863
915
|
...(relationNamespace !== undefined ? { nomosRelationNamespace: relationNamespace } : {}),
|
|
916
|
+
...(readProjectionNamespace !== undefined ? { nomosReadProjectionNamespace: readProjectionNamespace } : {}),
|
|
864
917
|
...(relationGate !== undefined ? { nomosRelationGate: relationGate } : {}),
|
|
865
918
|
...(attestationGate !== undefined ? { nomosAttestationGate: attestationGate } : {}),
|
|
866
919
|
...(birthGate !== undefined ? { nomosBirthGate: birthGate } : {}),
|
|
920
|
+
...(birthRecordGate !== undefined ? { nomosBirthRecordGate: birthRecordGate } : {}),
|
|
867
921
|
...(parentAttestedBirth !== undefined ? { nomosParentAttestedBirth: parentAttestedBirth } : {}),
|
|
868
922
|
...(verifiedFirstEnroll !== undefined ? { nomosVerifiedFirstEnroll: verifiedFirstEnroll } : {}),
|
|
923
|
+
...(valueObjects !== undefined ? { nomosValueObjects: valueObjects } : {}),
|
|
869
924
|
};
|
|
870
925
|
});
|
|
871
926
|
|
|
@@ -996,8 +1051,10 @@ export function parseUsdaDocument(text: string): UsdDocument {
|
|
|
996
1051
|
const relationsByDomain = new Map<string, UsdLayer["nomosRelations"]>();
|
|
997
1052
|
const relationGateByDomain = new Map<string, UsdLayer["nomosRelationGate"]>();
|
|
998
1053
|
const relationNamespaceByDomain = new Map<string, UsdLayer["nomosRelationNamespace"]>();
|
|
1054
|
+
const readProjectionNamespaceByDomain = new Map<string, UsdLayer["nomosReadProjectionNamespace"]>();
|
|
999
1055
|
const attestationGateByDomain = new Map<string, UsdLayer["nomosAttestationGate"]>();
|
|
1000
1056
|
const birthGateByDomain = new Map<string, UsdLayer["nomosBirthGate"]>();
|
|
1057
|
+
const birthRecordGateByDomain = new Map<string, UsdLayer["nomosBirthRecordGate"]>();
|
|
1001
1058
|
const parentAttestedBirthByDomain = new Map<string, UsdLayer["nomosParentAttestedBirth"]>();
|
|
1002
1059
|
const verifiedFirstEnrollByDomain = new Map<string, UsdLayer["nomosVerifiedFirstEnroll"]>();
|
|
1003
1060
|
const valueObjectsByDomain = new Map<string, UsdLayer["nomosValueObjects"]>();
|
|
@@ -1052,6 +1109,11 @@ export function parseUsdaDocument(text: string): UsdDocument {
|
|
|
1052
1109
|
if (typeof relNsHex === "string") {
|
|
1053
1110
|
relationNamespaceByDomain.set(domain, JSON.parse(unhexUtf8(relNsHex)) as UsdLayer["nomosRelationNamespace"]);
|
|
1054
1111
|
}
|
|
1112
|
+
// THE READ-PROJECTION-NAMESPACE ERA KEY: the hex-carried qualified-aggregate witness.
|
|
1113
|
+
const rpNsHex = p.attrs.get("nomos:readProjectionNamespaceHex");
|
|
1114
|
+
if (typeof rpNsHex === "string") {
|
|
1115
|
+
readProjectionNamespaceByDomain.set(domain, JSON.parse(unhexUtf8(rpNsHex)) as UsdLayer["nomosReadProjectionNamespace"]);
|
|
1116
|
+
}
|
|
1055
1117
|
// THE ATTESTATION GATE (home_holons §3/§5): the hex-carried attestation-authorized directive set.
|
|
1056
1118
|
const attGateHex = p.attrs.get("nomos:attestationGateHex");
|
|
1057
1119
|
if (typeof attGateHex === "string") {
|
|
@@ -1062,6 +1124,11 @@ export function parseUsdaDocument(text: string): UsdDocument {
|
|
|
1062
1124
|
if (typeof birthGateHex === "string") {
|
|
1063
1125
|
birthGateByDomain.set(domain, JSON.parse(unhexUtf8(birthGateHex)) as UsdLayer["nomosBirthGate"]);
|
|
1064
1126
|
}
|
|
1127
|
+
// THE BIRTH-RECORD GATE (metered birth, A1): the hex-carried record declaration.
|
|
1128
|
+
const birthRecordGateHex = p.attrs.get("nomos:birthRecordGateHex");
|
|
1129
|
+
if (typeof birthRecordGateHex === "string") {
|
|
1130
|
+
birthRecordGateByDomain.set(domain, JSON.parse(unhexUtf8(birthRecordGateHex)) as UsdLayer["nomosBirthRecordGate"]);
|
|
1131
|
+
}
|
|
1065
1132
|
// THE PEER-SOVEREIGN ROOT-OF-TRUST: the hex-carried parent-attested-birth era marker.
|
|
1066
1133
|
const pabHex = p.attrs.get("nomos:parentAttestedBirthHex");
|
|
1067
1134
|
if (typeof pabHex === "string") {
|
|
@@ -1174,8 +1241,10 @@ export function parseUsdaDocument(text: string): UsdDocument {
|
|
|
1174
1241
|
const rel = relationsByDomain.get(domain);
|
|
1175
1242
|
const relGate = relationGateByDomain.get(domain);
|
|
1176
1243
|
const relNs = relationNamespaceByDomain.get(domain);
|
|
1244
|
+
const rpNs = readProjectionNamespaceByDomain.get(domain);
|
|
1177
1245
|
const attGate = attestationGateByDomain.get(domain);
|
|
1178
1246
|
const birthGate = birthGateByDomain.get(domain);
|
|
1247
|
+
const birthRecordGate = birthRecordGateByDomain.get(domain);
|
|
1179
1248
|
const parentAttestedBirth = parentAttestedBirthByDomain.get(domain);
|
|
1180
1249
|
const verifiedFirstEnroll = verifiedFirstEnrollByDomain.get(domain);
|
|
1181
1250
|
const valueObjects = valueObjectsByDomain.get(domain);
|
|
@@ -1194,9 +1263,11 @@ export function parseUsdaDocument(text: string): UsdDocument {
|
|
|
1194
1263
|
...(enc !== undefined ? { nomosEncryption: enc } : {}),
|
|
1195
1264
|
...(rel !== undefined ? { nomosRelations: rel } : {}),
|
|
1196
1265
|
...(relNs !== undefined ? { nomosRelationNamespace: relNs } : {}),
|
|
1266
|
+
...(rpNs !== undefined ? { nomosReadProjectionNamespace: rpNs } : {}),
|
|
1197
1267
|
...(relGate !== undefined ? { nomosRelationGate: relGate } : {}),
|
|
1198
1268
|
...(attGate !== undefined ? { nomosAttestationGate: attGate } : {}),
|
|
1199
1269
|
...(birthGate !== undefined ? { nomosBirthGate: birthGate } : {}),
|
|
1270
|
+
...(birthRecordGate !== undefined ? { nomosBirthRecordGate: birthRecordGate } : {}),
|
|
1200
1271
|
...(parentAttestedBirth !== undefined ? { nomosParentAttestedBirth: parentAttestedBirth } : {}),
|
|
1201
1272
|
...(verifiedFirstEnroll !== undefined ? { nomosVerifiedFirstEnroll: verifiedFirstEnroll } : {}),
|
|
1202
1273
|
...(valueObjects !== undefined ? { nomosValueObjects: valueObjects } : {}),
|
package/src/value_object.ts
CHANGED
|
@@ -411,3 +411,46 @@ export function collectValueObjectDescriptors(
|
|
|
411
411
|
}
|
|
412
412
|
return [...byName.values()].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
413
413
|
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* ONE VO↔FIELD ATTACHMENT (the IR pointer): aggregate `aggregate`'s field `field`
|
|
417
|
+
* stores value object `vo`. Rides the additive `nomosValueObjects` USD key as
|
|
418
|
+
* `usages` so tooling (the scene projector, the explorer glyphs) can attach a VO
|
|
419
|
+
* molecule to the exact field that carries it. NEVER in the canonical
|
|
420
|
+
* manifest/domainHash — a VO field's wire schema stays a plain `jsonObject` leaf.
|
|
421
|
+
*/
|
|
422
|
+
export interface ValueObjectUsage {
|
|
423
|
+
readonly aggregate: string;
|
|
424
|
+
readonly field: string;
|
|
425
|
+
readonly vo: string;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Collect the sorted VO↔field usage pointers of a set of aggregates (one entry per
|
|
430
|
+
* VO-typed field; sorted by aggregate then field — byte-deterministic). Returns `[]`
|
|
431
|
+
* when no field carries a VO — the omit-when-empty input to the `usages` entry of
|
|
432
|
+
* the `nomosValueObjects` era key.
|
|
433
|
+
*/
|
|
434
|
+
export function collectValueObjectUsages(
|
|
435
|
+
aggregates: readonly { id: string; fields: Record<string, Field> }[],
|
|
436
|
+
): ValueObjectUsage[] {
|
|
437
|
+
const usages: ValueObjectUsage[] = [];
|
|
438
|
+
for (const agg of aggregates) {
|
|
439
|
+
for (const fieldName of Object.keys(agg.fields)) {
|
|
440
|
+
const meta = agg.fields[fieldName]!.valueObject;
|
|
441
|
+
if (meta === undefined) continue;
|
|
442
|
+
usages.push({ aggregate: agg.id, field: fieldName, vo: meta.name });
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return usages.sort((a, b) =>
|
|
446
|
+
a.aggregate !== b.aggregate
|
|
447
|
+
? a.aggregate < b.aggregate
|
|
448
|
+
? -1
|
|
449
|
+
: 1
|
|
450
|
+
: a.field < b.field
|
|
451
|
+
? -1
|
|
452
|
+
: a.field > b.field
|
|
453
|
+
? 1
|
|
454
|
+
: 0,
|
|
455
|
+
);
|
|
456
|
+
}
|
package/src/wire_encode.ts
CHANGED
|
@@ -20,9 +20,16 @@
|
|
|
20
20
|
* LATER coordinated migration — we target the current serde_json shape now.
|
|
21
21
|
*/
|
|
22
22
|
import { encodeDriver } from "./drivers.js";
|
|
23
|
+
import { __qualifyAggregateType } from "./framework/relations.js";
|
|
23
24
|
import { __resetAuthoring, __drainAuthoring } from "./authoring.js";
|
|
24
25
|
import { __resetReads, __drainReads } from "./read.js";
|
|
25
26
|
import { __resetBirths, __drainBirths } from "./births.js";
|
|
27
|
+
import {
|
|
28
|
+
birthMeterSubject,
|
|
29
|
+
birthMeterBornAt,
|
|
30
|
+
captureBirthMeterReads,
|
|
31
|
+
planBirthRecordOps,
|
|
32
|
+
} from "./birth_meter.js";
|
|
26
33
|
import type { AggregateHandle } from "./aggregate.js";
|
|
27
34
|
import type { Field, EvidenceRefValue } from "./fields.js";
|
|
28
35
|
import type { Directive, ReferentialMarker } from "./directive.js";
|
|
@@ -203,13 +210,33 @@ export function executeDirectiveToIntent<P>(
|
|
|
203
210
|
__resetReads();
|
|
204
211
|
__resetBirths();
|
|
205
212
|
const returned = directive.plan(parsed, ctx);
|
|
206
|
-
const planned = [...__drainAuthoring(), ...returned];
|
|
207
|
-
const reads = __drainReads();
|
|
208
213
|
// THE BIRTH EFFECT (G3 — birth is a declared kernel primitive): a `.births()` directive's plan records
|
|
209
214
|
// the child's genesis recipe via `birth(recipe)`; it rides the intent as a DECLARED output (like a
|
|
210
215
|
// captured read), drained here. Empty for every non-birth directive — OMITTED below so a birth-free
|
|
211
216
|
// intent is byte-identical (the era rule).
|
|
212
217
|
const births = __drainBirths();
|
|
218
|
+
// METERED BIRTH (slice A — architecture/metered_birth_and_attested_reads.md). For every PLANNED birth,
|
|
219
|
+
// the library — HERE, in the ONE plan-output assembly both lanes (client authoring + the sealed engine's
|
|
220
|
+
// `plan`) funnel through, so the halves are byte-identical by construction — does two things the tenant
|
|
221
|
+
// never writes:
|
|
222
|
+
// A1 THE AUTO-RECORD: append the `NomosBirthRecord` field ops (deterministic id per child; Lww upsert)
|
|
223
|
+
// to the SAME intent that carries the birth effect. The kernel's `nomosBirthRecordGate` era arm
|
|
224
|
+
// refuses a new-era birth whose intent carries no record — this is what satisfies it.
|
|
225
|
+
// A2 THE AUTO-CAPTURE: capture the three meter reads (records-by-subject + grant + policy — the #58
|
|
226
|
+
// lane `.births()` auto-declares) so the synthesized birth-quota invariant judges over COMMITTED,
|
|
227
|
+
// CAS-re-derived pre-apply state on every lane.
|
|
228
|
+
// A `.births()` dispatch that planned NO birth (e.g. birthChild without a genesis) appends/captures
|
|
229
|
+
// nothing — byte-identical to before (the era rule); pre-era bundles ship an older lump and are untouched.
|
|
230
|
+
const meterOps: PlannedOp[] = [];
|
|
231
|
+
if (births.length > 0) {
|
|
232
|
+
const payloadRecord = parsed as unknown as Record<string, unknown>;
|
|
233
|
+
const subject = birthMeterSubject(payloadRecord);
|
|
234
|
+
captureBirthMeterReads(subject);
|
|
235
|
+
const bornAt = birthMeterBornAt(payloadRecord);
|
|
236
|
+
for (const b of births) meterOps.push(...planBirthRecordOps(b, subject, bornAt));
|
|
237
|
+
}
|
|
238
|
+
const planned = [...__drainAuthoring(), ...returned, ...meterOps];
|
|
239
|
+
const reads = __drainReads();
|
|
213
240
|
|
|
214
241
|
// Partition the plan output: STRIKE ops route onto the intent's `strikes` channel
|
|
215
242
|
// (the kernel's strikeout / revert facet, folded by parity); FIELD ops group into
|
|
@@ -293,7 +320,11 @@ export function executeDirectiveToIntent<P>(
|
|
|
293
320
|
aggregate,
|
|
294
321
|
marker: eventMarker,
|
|
295
322
|
ops: [
|
|
296
|
-
|
|
323
|
+
// READ-PROJECTION NAMESPACING: the stamped TYPE is qualified by the dispatching domain iff
|
|
324
|
+
// the domain declares it non-global — so the folded row's `__type` equals the qualified
|
|
325
|
+
// schema key the SAME compile wrote (rows and read manifest agree by construction). Outside
|
|
326
|
+
// a plan dispatch (codegen probes) the type passes through bare.
|
|
327
|
+
{ field: "__type", op: { Set: { Str: __qualifyAggregateType(ty) } } },
|
|
297
328
|
{ field: "__id", op: { Set: { Str: aggregate } } },
|
|
298
329
|
...ops,
|
|
299
330
|
],
|