@githolon/dsl 0.33.0 → 0.34.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/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/compile_engine.ts +13 -2
- package/src/compile_package_main.ts +16 -0
- 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 +42 -1
- 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
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// NOMOS — Nomos Sovereign: participants act · verify · remember LOCALLY; hosted
|
|
2
|
+
// remotes are replaceable custody/transport, not truth. ⇒ ONE Nomos GitHolon
|
|
3
|
+
// wasm32-wasip1 artifact {kernel · SQLite projection · embedded QuickJS engine}.
|
|
4
|
+
//
|
|
5
|
+
// READ-PROJECTION NAMESPACING — the CANONICAL-MANIFEST label transform (the compile-side
|
|
6
|
+
// rename). BUILD-TIME ONLY, split out of `read_projection_namespace.ts` deliberately: this
|
|
7
|
+
// file types against `manifest.ts` (which imports `node:crypto`/`Buffer` for the domain
|
|
8
|
+
// hash), and the classification/qualifier half is imported by PLATFORM-NEUTRAL engine
|
|
9
|
+
// files (`engine_entry.ts`, `wire_encode.ts`, `framework/relations.ts`) that tenant
|
|
10
|
+
// scaffolds typecheck WITHOUT `@types/node`. Keeping the manifest edge here (reached only
|
|
11
|
+
// via `usd.ts`, the build lane) keeps the neutral type graph node-free — the same
|
|
12
|
+
// build/runtime split `stable_ids.ts` vs `stable_ids_types.ts` ratified.
|
|
13
|
+
|
|
14
|
+
import type { CanonicalManifest } from "./manifest.js";
|
|
15
|
+
import type { StableIds, StableAggregateId } from "./stable_ids_types.js";
|
|
16
|
+
import { qualifyReadProjectionType } from "./read_projection_namespace.js";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Qualify EVERY aggregate-type label a domain's canonical manifest carries — exactly as
|
|
20
|
+
* if the author had renamed `Entry` → `<domainKey>__Entry` in source, which is the #58
|
|
21
|
+
* rename the whole system already supports (sid identity unmoved; the projection folds
|
|
22
|
+
* old-label rows onto the new label via the `was` lineage). Touches: aggregate ids,
|
|
23
|
+
* directive targets, `queries[].returns`, every maintained read's `of` (counts / sums /
|
|
24
|
+
* mins / maxes / spatials / deriveds / combineds / orderedReads), `combineds[].reads`
|
|
25
|
+
* (a related aggregate TYPE — the cross-aggregate invalidation key), and the stable-ids
|
|
26
|
+
* labels (the BARE name is APPENDED to the sid's `was` lineage so pre-namespacing rows
|
|
27
|
+
* normalize onto the qualified label). Fields keep their bare labels (field names never
|
|
28
|
+
* collide across types — the accumulator keys them under the type). Pure + deterministic;
|
|
29
|
+
* with an empty `namespaced` set the manifest is returned UNTOUCHED (the era rule).
|
|
30
|
+
*/
|
|
31
|
+
export function qualifyManifestAggregateLabels(
|
|
32
|
+
manifest: CanonicalManifest,
|
|
33
|
+
domainKey: string,
|
|
34
|
+
namespaced: ReadonlySet<string>,
|
|
35
|
+
): CanonicalManifest {
|
|
36
|
+
if (namespaced.size === 0) return manifest;
|
|
37
|
+
const qa = (n: string): string => (namespaced.has(n) ? qualifyReadProjectionType(domainKey, n) : n);
|
|
38
|
+
|
|
39
|
+
const stableIds: StableIds | undefined =
|
|
40
|
+
manifest.stableIds === undefined
|
|
41
|
+
? undefined
|
|
42
|
+
: {
|
|
43
|
+
...manifest.stableIds,
|
|
44
|
+
aggregates: Object.fromEntries(
|
|
45
|
+
Object.entries(manifest.stableIds.aggregates).map(([name, rec]): [string, StableAggregateId] => {
|
|
46
|
+
if (!namespaced.has(name)) return [name, rec];
|
|
47
|
+
// The bare labels this sid's rows may carry: every prior bare label + the current
|
|
48
|
+
// bare name (pre-namespacing rows; the runtime stamp is qualified going forward).
|
|
49
|
+
const was = [...new Set([...(rec.was ?? []), name])];
|
|
50
|
+
return [qa(name), { ...rec, was }];
|
|
51
|
+
}),
|
|
52
|
+
),
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
...manifest,
|
|
57
|
+
aggregates: manifest.aggregates.map((a) => (namespaced.has(a.id) ? { ...a, id: qa(a.id) } : a)),
|
|
58
|
+
directives: manifest.directives.map((d) => (namespaced.has(d.target) ? { ...d, target: qa(d.target) } : d)),
|
|
59
|
+
...(stableIds !== undefined ? { stableIds } : {}),
|
|
60
|
+
...(manifest.queries !== undefined
|
|
61
|
+
? { queries: manifest.queries.map((q) => (namespaced.has(q.returns) ? { ...q, returns: qa(q.returns) } : q)) }
|
|
62
|
+
: {}),
|
|
63
|
+
...(manifest.counts !== undefined
|
|
64
|
+
? { counts: manifest.counts.map((c) => (namespaced.has(c.of) ? { ...c, of: qa(c.of) } : c)) }
|
|
65
|
+
: {}),
|
|
66
|
+
...(manifest.sums !== undefined
|
|
67
|
+
? { sums: manifest.sums.map((s) => (namespaced.has(s.of) ? { ...s, of: qa(s.of) } : s)) }
|
|
68
|
+
: {}),
|
|
69
|
+
...(manifest.mins !== undefined
|
|
70
|
+
? { mins: manifest.mins.map((e) => (namespaced.has(e.of) ? { ...e, of: qa(e.of) } : e)) }
|
|
71
|
+
: {}),
|
|
72
|
+
...(manifest.maxes !== undefined
|
|
73
|
+
? { maxes: manifest.maxes.map((e) => (namespaced.has(e.of) ? { ...e, of: qa(e.of) } : e)) }
|
|
74
|
+
: {}),
|
|
75
|
+
...(manifest.spatials !== undefined
|
|
76
|
+
? { spatials: manifest.spatials.map((s) => (namespaced.has(s.of) ? { ...s, of: qa(s.of) } : s)) }
|
|
77
|
+
: {}),
|
|
78
|
+
...(manifest.deriveds !== undefined
|
|
79
|
+
? { deriveds: manifest.deriveds.map((d) => (namespaced.has(d.of) ? { ...d, of: qa(d.of) } : d)) }
|
|
80
|
+
: {}),
|
|
81
|
+
...(manifest.combineds !== undefined
|
|
82
|
+
? {
|
|
83
|
+
combineds: manifest.combineds.map((c) => ({
|
|
84
|
+
...c,
|
|
85
|
+
...(namespaced.has(c.of) ? { of: qa(c.of) } : {}),
|
|
86
|
+
...(namespaced.has(c.reads) ? { reads: qa(c.reads) } : {}),
|
|
87
|
+
})),
|
|
88
|
+
}
|
|
89
|
+
: {}),
|
|
90
|
+
...(manifest.orderedReads !== undefined
|
|
91
|
+
? { orderedReads: manifest.orderedReads.map((r) => (namespaced.has(r.of) ? { ...r, of: qa(r.of) } : r)) }
|
|
92
|
+
: {}),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// NOMOS — Nomos Sovereign: participants act · verify · remember LOCALLY; hosted
|
|
2
|
+
// remotes are replaceable custody/transport, not truth. ⇒ ONE Nomos GitHolon
|
|
3
|
+
// wasm32-wasip1 artifact {kernel · SQLite projection · embedded QuickJS engine}.
|
|
4
|
+
//
|
|
5
|
+
// READ-PROJECTION (aggregate-type) NAMESPACING — the read-layer mirror of the shipped
|
|
6
|
+
// relation-type namespacing (`relation_schema.ts::qualifyRelationType`).
|
|
7
|
+
//
|
|
8
|
+
// THE DISEASE. The kernel's installed-read-manifest accumulator
|
|
9
|
+
// (`wasm_git_holon.rs::insert_read_projection_value`) unions every ACTIVE install's
|
|
10
|
+
// `nomosReadProjection` fragment and — correctly, fail-closed — REFUSES when two
|
|
11
|
+
// installs declare the SAME key with DIVERGENT shapes ("divergent read projection
|
|
12
|
+
// declaration for key AuthProvider"). Two different domain keys that each declare a
|
|
13
|
+
// same-named aggregate therefore WEDGE the workspace's read plane at serve time.
|
|
14
|
+
//
|
|
15
|
+
// THE CURE (the maintainer's rule: "Namespaces are a good idea. Use them."): a
|
|
16
|
+
// NON-GLOBAL aggregate's TYPE NAME is QUALIFIED by its owning domain key at compile —
|
|
17
|
+
// `Entry` in domain `guestbook` becomes `guestbook.Entry` — so two domains that each
|
|
18
|
+
// declare the same bare name NEVER share an accumulator key. The delimiter is `.`
|
|
19
|
+
// (NOT relations' `__`: the id-mint gate splits a minted id's type tag on the first
|
|
20
|
+
// `_`, and the type must also stay colon-free — see READ_PROJECTION_DELIMITER). The engine stamps the SAME qualified name onto every event's
|
|
21
|
+
// `__type` at plan runtime (see `wire_encode.ts`), so the projected row's type and the
|
|
22
|
+
// installed schema key agree by construction; the BARE name rides the aggregate's
|
|
23
|
+
// stable-id `was` lineage so pre-namespacing rows normalize onto the qualified label
|
|
24
|
+
// (the #58 rename machinery — a qualification IS a rename).
|
|
25
|
+
//
|
|
26
|
+
// GLOBAL (un-namespaced) aggregates — the opt-out. Framework aggregates the KERNEL
|
|
27
|
+
// reads by bare name / bare instance-id convention (AuthProvider for in-kernel JWT
|
|
28
|
+
// verification, RelationTuple for the native authz fold, the domain-lifecycle law
|
|
29
|
+
// records, …) MUST keep their bare identity. They are enumerated DELIBERATELY in the
|
|
30
|
+
// explicit allowlist below (never a heuristic); a drift test pins the list to the
|
|
31
|
+
// real framework exports. A dynamically-named framework aggregate (a factory product,
|
|
32
|
+
// e.g. `platformAdmin()`'s founder aggregate) opts out via `aggregate(id, fields,
|
|
33
|
+
// { global: true })` instead — the flag rides the handle into BOTH the compiler and
|
|
34
|
+
// the sealed engine, so the two classifications can never disagree.
|
|
35
|
+
//
|
|
36
|
+
// ERA RULE (the law is the era): qualification happens at COMPILE — already-deployed
|
|
37
|
+
// packages are byte-identical untouched, and a qualified key is just a NEW name to the
|
|
38
|
+
// kernel accumulator (no kernel change). The `nomosReadProjectionNamespace` era key is
|
|
39
|
+
// emitted IFF a layer qualified ≥1 aggregate — the marker that this bundle's read keys
|
|
40
|
+
// are domain-qualified (mirrors `nomosRelationNamespace`).
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The read-projection (aggregate-type) qualifier delimiter. NOT the relation namespacing's
|
|
44
|
+
* `__`, deliberately: an aggregate type is also the kernel id-mint TYPE TAG
|
|
45
|
+
* (`id-mint/src/lib.rs::type_tag_of` splits a minted id on the FIRST `_`), so an
|
|
46
|
+
* underscore-bearing type can never mint — `guestbook__Entry_uuid` parses as tag
|
|
47
|
+
* `guestbook` and refuses at the id-mint gate. And it must stay colon-free (`ObjectRef::
|
|
48
|
+
* parse` splits addresses on the first `:`). `.` satisfies both parsers. The ONE surface
|
|
49
|
+
* where `.` is illegal — a real-USD PRIM NAME (identifiers only) — is already covered by the
|
|
50
|
+
* `.usda` text lane's `primName()` sanitizer (`usd_layers.ts`), which round-trips the raw id
|
|
51
|
+
* via `nomos:id`; the TYPE STRING everywhere (prim `type` attr, directive `target`,
|
|
52
|
+
* read-projection keys, the runtime `__type` stamp, stable-id labels, the mint tag) is dotted.
|
|
53
|
+
*/
|
|
54
|
+
export const READ_PROJECTION_DELIMITER = ".";
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* THE EXPLICIT GLOBAL ALLOWLIST — every STATICALLY-NAMED framework aggregate. These
|
|
59
|
+
* stay BARE on every surface (read projection, prims, stable-ids, runtime `__type`)
|
|
60
|
+
* because the kernel and the framework machinery address them by bare name /
|
|
61
|
+
* bare-name-derived instance ids (`auth-provider:platform`, the relation fold, the
|
|
62
|
+
* policy/lifecycle law records, the signer spine, the key-fanout gate, …).
|
|
63
|
+
*
|
|
64
|
+
* DELIBERATE ENUMERATION, not a heuristic: `read_projection_namespace.test.ts` pins
|
|
65
|
+
* this set to the real `FRAMEWORK_AGGREGATE_IDS` registry so it can never drift.
|
|
66
|
+
* (A tenant aggregate that merely REUSES one of these names also stays bare — that
|
|
67
|
+
* matches the kernel-couples-by-name reality and is exactly the shared-key case the
|
|
68
|
+
* compile-time divergence guard `read_projection_composition.ts` polices.)
|
|
69
|
+
*/
|
|
70
|
+
export const GLOBAL_READ_PROJECTION_AGGREGATE_IDS: ReadonlySet<string> = new Set([
|
|
71
|
+
// disclosure
|
|
72
|
+
"Disclosure",
|
|
73
|
+
// domain lifecycle (the law records themselves)
|
|
74
|
+
"PolicyBundle",
|
|
75
|
+
"DomainInstallation",
|
|
76
|
+
// encryption identity + scope (the operator-blind key machinery)
|
|
77
|
+
"EncKey",
|
|
78
|
+
"KeyScope",
|
|
79
|
+
"WrappedKey",
|
|
80
|
+
// genesis / births
|
|
81
|
+
"GenesisWarrant",
|
|
82
|
+
// metered birth (slice A) — NomosBirthRecord is kernel-read by the birth-record
|
|
83
|
+
// gate (the `nomos-birth-record:` prefix); the policy/grant singletons ride with it
|
|
84
|
+
"NomosBirthRecord",
|
|
85
|
+
"BirthPolicy",
|
|
86
|
+
"BirthGrant",
|
|
87
|
+
// home holons
|
|
88
|
+
"HomeOwner",
|
|
89
|
+
"SharedWithMe",
|
|
90
|
+
"BirthAttestation",
|
|
91
|
+
// identity
|
|
92
|
+
"Actor",
|
|
93
|
+
"User",
|
|
94
|
+
"DeviceKey",
|
|
95
|
+
"Organization",
|
|
96
|
+
"PublicUser",
|
|
97
|
+
// native relations (the one authz spine)
|
|
98
|
+
"RelationTuple",
|
|
99
|
+
// repair
|
|
100
|
+
"RepairRecord",
|
|
101
|
+
// signer spine
|
|
102
|
+
"SignerEpoch",
|
|
103
|
+
"SignerDelegation",
|
|
104
|
+
// governance / workspaces
|
|
105
|
+
"WorkspaceSelf",
|
|
106
|
+
"CloudWorkspace",
|
|
107
|
+
"CloudPolicy",
|
|
108
|
+
"QuotaGrant",
|
|
109
|
+
"PlatformGrant",
|
|
110
|
+
"CapabilityBinding",
|
|
111
|
+
"AuthProvider",
|
|
112
|
+
"HostnameClaim",
|
|
113
|
+
]);
|
|
114
|
+
|
|
115
|
+
/** The minimal handle shape the classification reads (compiler AND engine — one function). */
|
|
116
|
+
export interface NamespaceClassifiable {
|
|
117
|
+
readonly id: string;
|
|
118
|
+
/** The `aggregate(id, fields, { global: true })` opt-out — kernel-coupled dynamic framework aggregates. */
|
|
119
|
+
readonly global?: boolean;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* TRUE iff this aggregate's type name is NAMESPACED (qualified by its owning domain).
|
|
124
|
+
* FALSE for the explicit framework allowlist and for `{ global: true }` handles.
|
|
125
|
+
* ONE classification, consumed by the compiler (USD lowering) and the sealed engine
|
|
126
|
+
* (the runtime `__type` stamp) — they can never disagree.
|
|
127
|
+
*/
|
|
128
|
+
export function isNamespacedAggregate(agg: NamespaceClassifiable): boolean {
|
|
129
|
+
return agg.global !== true && !GLOBAL_READ_PROJECTION_AGGREGATE_IDS.has(agg.id);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Qualify a bare aggregate type by its owning domain key: `("guestbook","Entry") → "guestbook.Entry"`.
|
|
134
|
+
* The domain-key half is MINT-TAG-SAFE-encoded (`_` → `-`): the qualified type is also the kernel
|
|
135
|
+
* id-mint TYPE TAG, and `type_tag_of` splits a minted id on the FIRST `_` — so NO underscore may
|
|
136
|
+
* appear anywhere in the tag (`co2_platform.X` would parse as tag `co2`). Deterministic; the era
|
|
137
|
+
* key witnesses the encoded form.
|
|
138
|
+
*/
|
|
139
|
+
export function qualifyReadProjectionType(domainKey: string, type: string): string {
|
|
140
|
+
return `${domainKey.replace(/_/g, "-")}${READ_PROJECTION_DELIMITER}${type}`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* The NAMESPACED (non-global) aggregate ids a module declares — the set whose type
|
|
145
|
+
* names qualify. Derived from the live handles (the `global` flag rides the handle),
|
|
146
|
+
* shared by `usd.ts` (compile) and `engine_entry.ts` (the dispatch context).
|
|
147
|
+
*/
|
|
148
|
+
export function namespacedAggregateIds(aggregates: readonly NamespaceClassifiable[]): Set<string> {
|
|
149
|
+
const out = new Set<string>();
|
|
150
|
+
for (const a of aggregates) if (isNamespacedAggregate(a)) out.add(a.id);
|
|
151
|
+
return out;
|
|
152
|
+
}
|
package/src/usd.ts
CHANGED
|
@@ -45,6 +45,14 @@ import { domainInterface } from "./shape_compat.js";
|
|
|
45
45
|
import { relationType, user, key, anyUser, qualifiedTypeId, qualifyRelationType } from "./relation_schema.js";
|
|
46
46
|
import { harvestPrincipalFlows } from "./wire_encode.js";
|
|
47
47
|
import { composeSharedRelationTypes } from "./relation_composition.js";
|
|
48
|
+
import { collectValueObjectUsages } from "./value_object.js";
|
|
49
|
+
import {
|
|
50
|
+
namespacedAggregateIds,
|
|
51
|
+
qualifyReadProjectionType,
|
|
52
|
+
READ_PROJECTION_DELIMITER,
|
|
53
|
+
} from "./read_projection_namespace.js";
|
|
54
|
+
import { qualifyManifestAggregateLabels } from "./read_projection_manifest.js";
|
|
55
|
+
import { guardSharedReadProjectionKeys } from "./read_projection_composition.js";
|
|
48
56
|
|
|
49
57
|
/** The referential marker of a directive prim (kernel `RefMode` wire string). */
|
|
50
58
|
export type RefMode = WireRefMode;
|
|
@@ -276,6 +284,22 @@ export interface UsdLayer {
|
|
|
276
284
|
readonly delimiter: string;
|
|
277
285
|
readonly types: readonly string[];
|
|
278
286
|
};
|
|
287
|
+
/**
|
|
288
|
+
* THE READ-PROJECTION NAMESPACE ERA KEY (read-projection namespacing — the read-layer mirror of
|
|
289
|
+
* `nomosRelationNamespace`). Present IFF this layer qualified ≥1 (non-global) AGGREGATE TYPE by its
|
|
290
|
+
* owning domain key (`Entry` → `guestbook__Entry`), so two domains / two installs that each declare
|
|
291
|
+
* the same bare aggregate name no longer collide at the kernel's installed-read-manifest accumulator
|
|
292
|
+
* ("divergent read projection declaration for key …" — the fail-closed union stays the trust
|
|
293
|
+
* boundary; a qualified key is simply a NEW name to it). `types` is the SORTED set of qualified type
|
|
294
|
+
* names this layer produced; `delimiter` is the same ratified colon-free `"__"`. OMITTED for a layer
|
|
295
|
+
* whose aggregates are all GLOBAL (the explicit framework allowlist / `{global:true}`) — the marker
|
|
296
|
+
* is a witness; the qualification itself is baked into the layer's keys.
|
|
297
|
+
*/
|
|
298
|
+
readonly nomosReadProjectionNamespace?: {
|
|
299
|
+
readonly v: 1;
|
|
300
|
+
readonly delimiter: string;
|
|
301
|
+
readonly types: readonly string[];
|
|
302
|
+
};
|
|
279
303
|
/**
|
|
280
304
|
* THE CROSS-CUSTODY ATTESTATION GATE (home_holons §3/§5). Present IFF ≥1 directive declares
|
|
281
305
|
* `.attestationAuthorized()`: the SORTED set of their ids. A directive named here is authorized by a
|
|
@@ -339,6 +363,25 @@ export interface UsdLayer {
|
|
|
339
363
|
/** The directive ids declared `.births()` — SORTED. */
|
|
340
364
|
readonly directives: string[];
|
|
341
365
|
};
|
|
366
|
+
/**
|
|
367
|
+
* THE BIRTH-RECORD GATE (metered birth, slice A1 — `architecture/metered_birth_and_attested_reads.md`).
|
|
368
|
+
* Present IFF the layer declares ≥1 `.births()` directive AND was compiled under the metered-birth
|
|
369
|
+
* compiler: the law's EXPLICIT declaration that every birth it performs ALSO writes a structural
|
|
370
|
+
* `NomosBirthRecord` event (deterministic id `<prefix><child>`) in the SAME intent as the birth
|
|
371
|
+
* effect — the `.births()` lane appends it automatically, so the declaration is self-fulfilling for
|
|
372
|
+
* honestly-compiled law and the kernel arm only ever bites a forged/hand-built lump. The kernel's
|
|
373
|
+
* `verify_births_witness` reads THIS declaration (the SAME way it reads `nomosBirthGate` — the
|
|
374
|
+
* bundle declares, the kernel honors; NO kernel version-if) and refuses a new-era birth whose
|
|
375
|
+
* re-derived events carry no record (`birth-record-missing`). OMITTED for pre-feature law — the
|
|
376
|
+
* era rule: every chain sealed before this key replays byte-identical, births exactly as today.
|
|
377
|
+
*/
|
|
378
|
+
readonly nomosBirthRecordGate?: {
|
|
379
|
+
readonly v: 1;
|
|
380
|
+
/** The record aggregate TYPE name (the framework's `NomosBirthRecord`). */
|
|
381
|
+
readonly aggregate: string;
|
|
382
|
+
/** The deterministic instance-id prefix — the record for child `w` lives at `<prefix><w>`. */
|
|
383
|
+
readonly prefix: string;
|
|
384
|
+
};
|
|
342
385
|
/**
|
|
343
386
|
* THE PEER-SOVEREIGN ROOT-OF-TRUST (the BirthAttestation cert chain). Present IFF the law declares
|
|
344
387
|
* `requireParentAttestedBirth`: a warranted workspace running this law MUST travel with a SIGNED cert
|
|
@@ -476,6 +519,14 @@ export interface UsdLayer {
|
|
|
476
519
|
readonly nomosValueObjects?: {
|
|
477
520
|
readonly v: 1;
|
|
478
521
|
readonly valueObjects: readonly import("./fields.js").ValueObjectDescriptor[];
|
|
522
|
+
/**
|
|
523
|
+
* THE VO↔FIELD ATTACHMENT (additive, hash-inert): which aggregate FIELD carries
|
|
524
|
+
* which VO — `{aggregate, field, vo}` sorted by aggregate then field. Rides THIS
|
|
525
|
+
* key (never the aggregate prim's wire schema) so the canonical manifest/domainHash
|
|
526
|
+
* of a VO field stays byte-equal to a plain `t.jsonObject()` field. Optional for
|
|
527
|
+
* era tolerance: a 0.33.0-compiled package's key parses unchanged without it.
|
|
528
|
+
*/
|
|
529
|
+
readonly usages?: readonly import("./value_object.js").ValueObjectUsage[];
|
|
479
530
|
};
|
|
480
531
|
}
|
|
481
532
|
|
|
@@ -499,13 +550,15 @@ function sortPrims(prims: UsdPrim[]): UsdPrim[] {
|
|
|
499
550
|
* encoded schema; directive prims map `requiresCapability` → `requires: [cap]`, leave
|
|
500
551
|
* `scope`/`payloadRef` `undefined` (no static scope today; payload is stage 2).
|
|
501
552
|
*/
|
|
502
|
-
function encodeModuleToPrims(layerPath: string,
|
|
503
|
-
const manifest = domainManifest(mod);
|
|
553
|
+
function encodeModuleToPrims(layerPath: string, manifest: CanonicalManifest): UsdPrim[] {
|
|
504
554
|
const prims: UsdPrim[] = [];
|
|
505
555
|
|
|
506
556
|
for (const agg of manifest.aggregates) {
|
|
507
557
|
prims.push({
|
|
508
558
|
kind: "aggregate",
|
|
559
|
+
// The IR prim path carries the (possibly dotted-qualified) type verbatim; the `.usda`
|
|
560
|
+
// TEXT lane sanitizes the prim NAME (`usd_layers.ts::primName`) and round-trips the raw
|
|
561
|
+
// id via `nomos:id`, so real-USD identifier rules are already satisfied downstream.
|
|
509
562
|
path: `${layerPath}/${agg.id}`,
|
|
510
563
|
type: agg.id,
|
|
511
564
|
schema: agg.schema,
|
|
@@ -538,6 +591,7 @@ function encodeModuleToPrims(layerPath: string, mod: DomainModule): UsdPrim[] {
|
|
|
538
591
|
|
|
539
592
|
function aggregateFieldKindsForModule(
|
|
540
593
|
mod: DomainModule,
|
|
594
|
+
qa: (type: string) => string = (n) => n,
|
|
541
595
|
): Record<string, Record<string, UsdFieldKindDescriptor>> {
|
|
542
596
|
const out: Record<string, Record<string, UsdFieldKindDescriptor>> = {};
|
|
543
597
|
for (const agg of [...(mod.aggregates as AggregateHandle[])].sort((a, b) =>
|
|
@@ -551,7 +605,7 @@ function aggregateFieldKindsForModule(
|
|
|
551
605
|
...(field.mapValueKind !== undefined ? { mapValueKind: field.mapValueKind } : {}),
|
|
552
606
|
};
|
|
553
607
|
}
|
|
554
|
-
out[agg.id] = fields;
|
|
608
|
+
out[qa(agg.id)] = fields;
|
|
555
609
|
}
|
|
556
610
|
return out;
|
|
557
611
|
}
|
|
@@ -578,8 +632,12 @@ function readKeyFieldKind(
|
|
|
578
632
|
return descriptor.kind;
|
|
579
633
|
}
|
|
580
634
|
|
|
581
|
-
function readProjectionForModule(
|
|
582
|
-
|
|
635
|
+
function readProjectionForModule(
|
|
636
|
+
mod: DomainModule,
|
|
637
|
+
manifest: CanonicalManifest,
|
|
638
|
+
qa: (type: string) => string = (n) => n,
|
|
639
|
+
): UsdReadProjection {
|
|
640
|
+
const aggregateFieldKinds = aggregateFieldKindsForModule(mod, qa);
|
|
583
641
|
return {
|
|
584
642
|
aggregateFieldKinds,
|
|
585
643
|
queries: (manifest.queries ?? []).map((q) => ({
|
|
@@ -743,13 +801,35 @@ export function emitUsd(
|
|
|
743
801
|
// Carry the domain's declared queries straight from the canonical manifest,
|
|
744
802
|
// which already applies omit-when-empty: `queries` is present here ONLY when the
|
|
745
803
|
// module declared them, so a query-free layer is byte-identical to before.
|
|
746
|
-
const
|
|
804
|
+
const bareManifest = domainManifest(l.module);
|
|
805
|
+
// READ-PROJECTION NAMESPACING: every NON-GLOBAL aggregate's type name is qualified by the
|
|
806
|
+
// owning domain key — on the prims, the identity manifest, the stable-ids labels (bare name
|
|
807
|
+
// appended to `was` — the rename lineage), the read projection, and every `of`/`returns`
|
|
808
|
+
// reference — so two domains / two installs that each declare the same bare name never share
|
|
809
|
+
// an installed-read-manifest accumulator key. GLOBAL (framework allowlist / `{global:true}`)
|
|
810
|
+
// aggregates stay bare (the kernel addresses them by bare name). The engine stamps the SAME
|
|
811
|
+
// qualified name at plan runtime (`wire_encode.ts` via the dispatch context), so rows and
|
|
812
|
+
// schema keys agree by construction. `module.domain ?? module.name` — the SAME qualifier key
|
|
813
|
+
// relation namespacing uses (=== `intent.domain` at plan runtime).
|
|
814
|
+
const rpDomainKey = l.module.domain ?? l.module.name;
|
|
815
|
+
const namespacedAggs = namespacedAggregateIds(
|
|
816
|
+
l.module.aggregates as unknown as readonly { id: string; global?: boolean }[],
|
|
817
|
+
);
|
|
818
|
+
const qaType = (n: string): string =>
|
|
819
|
+
namespacedAggs.has(n) ? qualifyReadProjectionType(rpDomainKey, n) : n;
|
|
820
|
+
const manifest = qualifyManifestAggregateLabels(bareManifest, rpDomainKey, namespacedAggs);
|
|
821
|
+
// THE READ-PROJECTION NAMESPACE ERA KEY: present iff ≥1 aggregate was qualified (the witness
|
|
822
|
+
// marker — mirrors `nomosRelationNamespace`; the qualification itself is baked into the keys).
|
|
823
|
+
const nomosReadProjectionNamespace =
|
|
824
|
+
namespacedAggs.size > 0
|
|
825
|
+
? { v: 1 as const, delimiter: READ_PROJECTION_DELIMITER, types: [...namespacedAggs].map(qaType).sort() }
|
|
826
|
+
: undefined;
|
|
747
827
|
// THE INVARIANT-GATE DECLARATION (#41): presence-only, derived from the SAME module
|
|
748
828
|
// the lump's executable bodies come from — sorted, omit-when-empty (hash-stable for
|
|
749
829
|
// invariant-free law; see the `UsdLayer.nomosInvariantGate` banner for the era rule).
|
|
750
830
|
const invariantAggregates = l.module.aggregates
|
|
751
831
|
.filter((a) => a.hasInvariant === true)
|
|
752
|
-
.map((a) => a.id)
|
|
832
|
+
.map((a) => qaType(a.id))
|
|
753
833
|
.sort();
|
|
754
834
|
const wsInvariants = [...(l.module.workspaceInvariants ?? [])]
|
|
755
835
|
.map((w) => ({ id: w.id, on: w.on }))
|
|
@@ -882,6 +962,17 @@ export function emitUsd(
|
|
|
882
962
|
.sort();
|
|
883
963
|
const nomosBirthGate =
|
|
884
964
|
birthDirectives.length > 0 ? { v: 1 as const, directives: birthDirectives } : undefined;
|
|
965
|
+
// THE BIRTH-RECORD GATE (metered birth, slice A1 — the framework's new era key). Present IFF
|
|
966
|
+
// this layer declares ≥1 `.births()` directive AND was compiled under this compiler: the law's
|
|
967
|
+
// EXPLICIT declaration that every birth it performs ALSO writes a structural NomosBirthRecord
|
|
968
|
+
// in the SAME intent (the `.births()` lane appends it automatically). The kernel's
|
|
969
|
+
// `verify_births_witness` reads THIS key (never a version-if): a carried birth whose re-derived
|
|
970
|
+
// events contain no `<prefix><child>` record event is a typed refusal (`birth-record-missing`).
|
|
971
|
+
// Omit-when-empty (the era rule): every pre-feature bundle lacks the key and births as today.
|
|
972
|
+
const nomosBirthRecordGate =
|
|
973
|
+
birthDirectives.length > 0
|
|
974
|
+
? { v: 1 as const, aggregate: "NomosBirthRecord", prefix: "nomos-birth-record:" }
|
|
975
|
+
: undefined;
|
|
885
976
|
// THE PEER-SOVEREIGN ROOT-OF-TRUST: the home/child law declared it requires a parent-attested-birth
|
|
886
977
|
// cert chain (the `requireParentAttestedBirth` sentinel). Emit the era key with the LAW-DECLARED owner +
|
|
887
978
|
// cert locators (so the kernel reads them, names neither); a bare marker (no locators) falls back to the
|
|
@@ -1011,9 +1102,18 @@ export function emitUsd(
|
|
|
1011
1102
|
// FIRST-CLASS VALUE OBJECTS: the module's collected descriptors (composeDomainModule
|
|
1012
1103
|
// dedupes + sorts them). Omit-when-empty (era rule — VO-free law is byte-identical).
|
|
1013
1104
|
const moduleValueObjects = (l.module as { valueObjects?: readonly import("./fields.js").ValueObjectDescriptor[] }).valueObjects;
|
|
1105
|
+
// THE VO↔FIELD ATTACHMENT: the usage pointers ride the SAME additive key (see
|
|
1106
|
+
// the UsdLayer type) — computed from the live Field metas, sorted, omit-when-empty.
|
|
1107
|
+
const voUsages = collectValueObjectUsages(
|
|
1108
|
+
l.module.aggregates as unknown as readonly { id: string; fields: Record<string, import("./fields.js").Field> }[],
|
|
1109
|
+
);
|
|
1014
1110
|
const nomosValueObjects =
|
|
1015
1111
|
moduleValueObjects !== undefined && moduleValueObjects.length > 0
|
|
1016
|
-
? {
|
|
1112
|
+
? {
|
|
1113
|
+
v: 1 as const,
|
|
1114
|
+
valueObjects: [...moduleValueObjects],
|
|
1115
|
+
...(voUsages.length > 0 ? { usages: voUsages } : {}),
|
|
1116
|
+
}
|
|
1017
1117
|
: undefined;
|
|
1018
1118
|
const flowEndorse = [...l.module.directives]
|
|
1019
1119
|
.flatMap((d) => harvestPrincipalFlows(d as never).map((f) => ({ directiveId: d.id, sink: f.sink, facet: f.facet, reason: f.reason })))
|
|
@@ -1024,9 +1124,9 @@ export function emitUsd(
|
|
|
1024
1124
|
);
|
|
1025
1125
|
return {
|
|
1026
1126
|
path: l.path,
|
|
1027
|
-
prims: encodeModuleToPrims(l.path,
|
|
1127
|
+
prims: encodeModuleToPrims(l.path, manifest),
|
|
1028
1128
|
nomosIdentityManifest: manifest,
|
|
1029
|
-
nomosReadProjection: readProjectionForModule(l.module, manifest),
|
|
1129
|
+
nomosReadProjection: readProjectionForModule(l.module, manifest, qaType),
|
|
1030
1130
|
...(manifest.queries !== undefined ? { queries: manifest.queries } : {}),
|
|
1031
1131
|
...(manifest.deriveds !== undefined ? { deriveds: manifest.deriveds } : {}),
|
|
1032
1132
|
...(manifest.combineds !== undefined ? { combineds: manifest.combineds } : {}),
|
|
@@ -1034,7 +1134,9 @@ export function emitUsd(
|
|
|
1034
1134
|
...(manifest.stableIds !== undefined ? { nomosStableIds: manifest.stableIds } : {}),
|
|
1035
1135
|
// THE STRUCTURAL INTERFACE (#72): the on-ledger OFFER — a client negotiates shape-
|
|
1036
1136
|
// compatibility against this, reactively, straight from the folded `policy:{hash}.bundle`.
|
|
1037
|
-
|
|
1137
|
+
// The client-facing structural offer stays BARE-labelled (labels are cosmetic there;
|
|
1138
|
+
// shape-compat negotiates by sid) — pass the pre-namespacing stable-ids.
|
|
1139
|
+
nomosInterface: domainInterface(l.module, bareManifest.stableIds),
|
|
1038
1140
|
...(Object.keys(readGateDirectives).length > 0
|
|
1039
1141
|
? {
|
|
1040
1142
|
nomosReadGate: {
|
|
@@ -1055,9 +1157,11 @@ export function emitUsd(
|
|
|
1055
1157
|
: {}),
|
|
1056
1158
|
...(nomosRelations !== undefined ? { nomosRelations } : {}),
|
|
1057
1159
|
...(nomosRelationNamespace !== undefined ? { nomosRelationNamespace } : {}),
|
|
1160
|
+
...(nomosReadProjectionNamespace !== undefined ? { nomosReadProjectionNamespace } : {}),
|
|
1058
1161
|
...(nomosRelationGate !== undefined ? { nomosRelationGate } : {}),
|
|
1059
1162
|
...(nomosAttestationGate !== undefined ? { nomosAttestationGate } : {}),
|
|
1060
1163
|
...(nomosBirthGate !== undefined ? { nomosBirthGate } : {}),
|
|
1164
|
+
...(nomosBirthRecordGate !== undefined ? { nomosBirthRecordGate } : {}),
|
|
1061
1165
|
...(nomosParentAttestedBirth !== undefined ? { nomosParentAttestedBirth } : {}),
|
|
1062
1166
|
...(nomosVerifiedFirstEnroll !== undefined ? { nomosVerifiedFirstEnroll } : {}),
|
|
1063
1167
|
...(nomosValueObjects !== undefined ? { nomosValueObjects } : {}),
|
|
@@ -1086,6 +1190,12 @@ export function emitUsd(
|
|
|
1086
1190
|
// package's per-domain layers: UNION every domain's relations on a shared bare-id type (so nothing
|
|
1087
1191
|
// is silently dropped) and REFUSE on a divergent shared rewrite (the scalpel). Non-shared /
|
|
1088
1192
|
// auto-qualified types are untouched → a package with no shared global type is byte-identical.
|
|
1193
|
+
// THE READ-PROJECTION DIVERGENCE GUARD (the scalpel, extended to the read layer): for keys that
|
|
1194
|
+
// remain SHARED across this package's domains (GLOBAL framework aggregates / same-named declared
|
|
1195
|
+
// reads), identical declarations dedupe at the kernel accumulator; a DIVERGENT shared key would
|
|
1196
|
+
// wedge the workspace at serve time — refuse it AT COMPILE instead, naming the key and each
|
|
1197
|
+
// domain's shape. Validation only (never transforms), so a conflict-free package is untouched.
|
|
1198
|
+
guardSharedReadProjectionKeys(out);
|
|
1089
1199
|
return { layers: composeSharedRelationTypes(out) };
|
|
1090
1200
|
}
|
|
1091
1201
|
|
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 } : {}),
|