@githolon/dsl 0.102.1 → 0.102.2
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 +9 -0
- package/dart/lib/src/dev_cloud.dart +61 -18
- package/dart/lib/src/runner/node-entry.mjs +27 -6
- package/dart/lib/src/runner/offline-install.mjs +80 -18
- package/dart/lib/src/test_harness.dart +874 -554
- package/dart/pubspec.yaml +1 -1
- package/package.json +1 -1
- package/runtime/generated-application-host.js +14 -6
- package/src/application_composition.ts +117 -2
- package/src/business_birth.ts +11 -9
- package/src/codegen_dart_application.ts +28 -24
- package/src/codegen_dart_scenario.ts +68 -9
- package/src/compile_package_main.ts +168 -42
- package/src/framework/home.ts +7 -1
- package/src/law_check.ts +8 -0
- package/src/usd_layers.ts +68 -51
- package/src/workspace_sharing.ts +5 -3
|
@@ -262,8 +262,9 @@ interface PackageConfig {
|
|
|
262
262
|
* • `test` — optional; generated business scenarios for widget/integration tests.
|
|
263
263
|
* The layering line: UI code depends on `types`; composition/runtime code on `client`;
|
|
264
264
|
* generated TYPES may flow to the UI, generated CLIENTS may not.
|
|
265
|
-
* Each package's `pubspec.yaml`
|
|
266
|
-
*
|
|
265
|
+
* Each package's `pubspec.yaml` keeps tenant-owned versioning/publishing metadata and custom
|
|
266
|
+
* dependencies; Nomos-owned framework dependency floors are reconciled on every compile.
|
|
267
|
+
* Barrels + `lib/src/generated/` are generated-owned and rewritten every compile.
|
|
267
268
|
* Requires `support: "package"` (vendored mode contradicts pub-package layering).
|
|
268
269
|
*/
|
|
269
270
|
readonly packages?: {
|
|
@@ -1298,12 +1299,27 @@ function emitDart(
|
|
|
1298
1299
|
// ── the LAYERED-PACKAGES Dart emit (dart.packages: blast-radius isolation) ────────
|
|
1299
1300
|
|
|
1300
1301
|
/** The pure-core support import generated TYPES files carry in layered-packages mode. */
|
|
1301
|
-
const
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
]
|
|
1302
|
+
const dartTypesImportFor = (generatedSource: string): string => {
|
|
1303
|
+
const lines = [
|
|
1304
|
+
`import 'package:nomos_types/nomos_types.dart';`,
|
|
1305
|
+
...(generatedSource.includes("NmsInternalNomosRefs.")
|
|
1306
|
+
? [`import 'package:nomos_types/src/nomos_ref.dart' show NmsInternalNomosRefs;`]
|
|
1307
|
+
: []),
|
|
1308
|
+
...(generatedSource.includes("NmsInternalForeignRefs.")
|
|
1309
|
+
? [`import 'package:nomos_types/src/foreign_ref.dart' show NmsInternalForeignRefs;`]
|
|
1310
|
+
: []),
|
|
1311
|
+
];
|
|
1312
|
+
const invitationHelpers = [
|
|
1313
|
+
...(generatedSource.includes("NmsInternalInvitations.") ? ["NmsInternalInvitations"] : []),
|
|
1314
|
+
...(generatedSource.includes("NmsInternalRecipients.") ? ["NmsInternalRecipients"] : []),
|
|
1315
|
+
];
|
|
1316
|
+
if (invitationHelpers.length > 0) {
|
|
1317
|
+
lines.push(
|
|
1318
|
+
`import 'package:nomos_types/src/invitation.dart' show ${invitationHelpers.join(", ")};`,
|
|
1319
|
+
);
|
|
1320
|
+
}
|
|
1321
|
+
return lines.join("\n");
|
|
1322
|
+
};
|
|
1307
1323
|
/** Version constraints the scaffolded pubspecs pin — move together with the Dart packages' releases.
|
|
1308
1324
|
* Caret on 0.x is EXACT-MINOR (`^0.45.0` = `<0.46.0`), so these must track the CURRENT release, not
|
|
1309
1325
|
* just the floor generated code needs — a lagging pin forces tenants into dependency_overrides. */
|
|
@@ -1313,8 +1329,8 @@ const NOMOS_TYPES_DEP = "^0.8.0";
|
|
|
1313
1329
|
// 0.48.x: session-bound clients (XClient.bind/NomosClientInterface/DomainClientSessionMismatch/
|
|
1314
1330
|
// DomainNotInstalledForSession, engine.follow, pendingConflicts lanes) — the generated client
|
|
1315
1331
|
// REQUIRES this surface. 0.48.1 fixes NomosSession.watchPendingConflicts delegation.
|
|
1316
|
-
const NOMOS_CLIENT_DEP = "^0.64.
|
|
1317
|
-
const NOMOS_FLUTTER_DEP = "^0.63.
|
|
1332
|
+
const NOMOS_CLIENT_DEP = "^0.64.1";
|
|
1333
|
+
const NOMOS_FLUTTER_DEP = "^0.63.1";
|
|
1318
1334
|
// DRIFT TRIPWIRE: pubspec_pins.test.ts asserts these equal the CURRENT versions in dsl/dart/pubspec.yaml
|
|
1319
1335
|
// and dsl/dart_types/pubspec.yaml — a Dart release without this bump fails the suite, so tenants never
|
|
1320
1336
|
// land back in dependency_overrides (the exact co2 report this closes, third occurrence).
|
|
@@ -1690,13 +1706,59 @@ function cursedNameHints(declared: ReadonlySet<string>, names: Readonly<Record<s
|
|
|
1690
1706
|
.sort((a, b) => b.length - a.length);
|
|
1691
1707
|
}
|
|
1692
1708
|
|
|
1693
|
-
/** Scaffold a pubspec
|
|
1694
|
-
*
|
|
1695
|
-
|
|
1709
|
+
/** Scaffold a pubspec when missing, otherwise reconcile only the dependencies owned by Nomos.
|
|
1710
|
+
* Tenant versioning, publishing metadata and application dependencies remain untouched, while a
|
|
1711
|
+
* regenerated client can never silently retain an obsolete framework floor. */
|
|
1712
|
+
function reconcilePubspec(
|
|
1713
|
+
pkgDir: string,
|
|
1714
|
+
yaml: string,
|
|
1715
|
+
managedDependencies: Readonly<Record<string, string>> = {},
|
|
1716
|
+
): boolean {
|
|
1696
1717
|
const p = path.join(pkgDir, "pubspec.yaml");
|
|
1697
|
-
if (existsSync(p))
|
|
1698
|
-
|
|
1699
|
-
|
|
1718
|
+
if (!existsSync(p)) {
|
|
1719
|
+
mkdirSync(pkgDir, { recursive: true });
|
|
1720
|
+
writeFileSync(p, yaml, "utf8");
|
|
1721
|
+
return true;
|
|
1722
|
+
}
|
|
1723
|
+
if (Object.keys(managedDependencies).length === 0) return false;
|
|
1724
|
+
|
|
1725
|
+
const source = readFileSync(p, "utf8");
|
|
1726
|
+
const trailingNewline = source.endsWith("\n");
|
|
1727
|
+
const lines = source.split(/\r?\n/);
|
|
1728
|
+
if (trailingNewline) lines.pop();
|
|
1729
|
+
let dependenciesStart = lines.findIndex((line) => /^dependencies:\s*(?:#.*)?$/.test(line));
|
|
1730
|
+
if (dependenciesStart < 0) {
|
|
1731
|
+
const before = lines.findIndex((line) => /^dev_dependencies:\s*(?:#.*)?$/.test(line));
|
|
1732
|
+
dependenciesStart = before < 0 ? lines.length : before;
|
|
1733
|
+
lines.splice(dependenciesStart, 0, "dependencies:");
|
|
1734
|
+
}
|
|
1735
|
+
let dependenciesEnd = dependenciesStart + 1;
|
|
1736
|
+
while (dependenciesEnd < lines.length) {
|
|
1737
|
+
const line = lines[dependenciesEnd]!;
|
|
1738
|
+
if (line.trim() !== "" && !/^\s/.test(line) && !/^\s*#/.test(line)) break;
|
|
1739
|
+
dependenciesEnd += 1;
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
for (const [name, constraint] of Object.entries(managedDependencies)) {
|
|
1743
|
+
const prefix = ` ${name}:`;
|
|
1744
|
+
const existing = lines.findIndex(
|
|
1745
|
+
(line, index) => index > dependenciesStart && index < dependenciesEnd && line.startsWith(prefix),
|
|
1746
|
+
);
|
|
1747
|
+
const replacement = ` ${name}: ${constraint}`;
|
|
1748
|
+
if (existing < 0) {
|
|
1749
|
+
lines.splice(dependenciesEnd, 0, replacement);
|
|
1750
|
+
dependenciesEnd += 1;
|
|
1751
|
+
continue;
|
|
1752
|
+
}
|
|
1753
|
+
let nestedEnd = existing + 1;
|
|
1754
|
+
while (nestedEnd < dependenciesEnd && /^(?: {4,}|\t)/.test(lines[nestedEnd]!)) nestedEnd += 1;
|
|
1755
|
+
lines.splice(existing, nestedEnd - existing, replacement);
|
|
1756
|
+
dependenciesEnd -= nestedEnd - existing - 1;
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
const next = `${lines.join("\n")}${trailingNewline ? "\n" : ""}`;
|
|
1760
|
+
if (next === source) return false;
|
|
1761
|
+
writeFileSync(p, next, "utf8");
|
|
1700
1762
|
return true;
|
|
1701
1763
|
}
|
|
1702
1764
|
|
|
@@ -1705,7 +1767,8 @@ function scaffoldPubspec(pkgDir: string, yaml: string): boolean {
|
|
|
1705
1767
|
* `<types>/` (pure data, depends only on `nomos_types`), `<client>/` (effectful runtime clients,
|
|
1706
1768
|
* depends on the types package + `nomos_client`), and optionally `<test>/` (the headless-harness
|
|
1707
1769
|
* surface). Generated code lives under each package's `lib/src/generated/` (wiped per compile)
|
|
1708
|
-
* behind a generated-owned barrel `lib/<pkg>.dart`;
|
|
1770
|
+
* behind a generated-owned barrel `lib/<pkg>.dart`; pubspec framework floors are compiler-owned while
|
|
1771
|
+
* tenant versioning, publishing metadata and application dependencies survive regeneration.
|
|
1709
1772
|
* TYPES-FILE PURITY is enforced fail-closed: a generated types file may import ONLY the
|
|
1710
1773
|
* `nomos_types` barrel and the relative `nomos_framework.dart` — any runtime import is a compile
|
|
1711
1774
|
* failure here, not a review comment downstream.
|
|
@@ -1760,7 +1823,7 @@ function emitDartPackages(
|
|
|
1760
1823
|
if (!src.includes(DART_PACKAGE_IMPORT)) {
|
|
1761
1824
|
fail(`generated Dart for '${fileBase}' does not carry the expected support import — codegen_dart header drifted`);
|
|
1762
1825
|
}
|
|
1763
|
-
const pure = src.replace(DART_PACKAGE_IMPORT,
|
|
1826
|
+
const pure = src.replace(DART_PACKAGE_IMPORT, dartTypesImportFor(src));
|
|
1764
1827
|
// PURITY, fail-closed: only the nomos_types barrel, the framework file, and SIBLING generated type files
|
|
1765
1828
|
// (cross-domain foreignRef targets) may be imported. Runtime/bridge surfaces (nomos_client) stay out.
|
|
1766
1829
|
for (const line of pure.split("\n")) {
|
|
@@ -1954,7 +2017,7 @@ function emitDartPackages(
|
|
|
1954
2017
|
const typesBarrelPath = path.join(pkgDir(packages.types), "lib", `${packages.types}.dart`);
|
|
1955
2018
|
writeFileSync(typesBarrelPath, typesBarrel.join("\n"), "utf8");
|
|
1956
2019
|
written.push(typesBarrelPath);
|
|
1957
|
-
|
|
2020
|
+
reconcilePubspec(
|
|
1958
2021
|
pkgDir(packages.types),
|
|
1959
2022
|
[
|
|
1960
2023
|
`name: ${packages.types}`,
|
|
@@ -1967,6 +2030,7 @@ function emitDartPackages(
|
|
|
1967
2030
|
` nomos_types: ${NOMOS_TYPES_DEP}`,
|
|
1968
2031
|
``,
|
|
1969
2032
|
].join("\n"),
|
|
2033
|
+
{ nomos_types: NOMOS_TYPES_DEP },
|
|
1970
2034
|
);
|
|
1971
2035
|
|
|
1972
2036
|
// ── the CLIENT package — effectful runtime, depends on types + nomos_client ──
|
|
@@ -2110,7 +2174,7 @@ function emitDartPackages(
|
|
|
2110
2174
|
const clientBarrelPath = path.join(pkgDir(packages.client), "lib", `${packages.client}.dart`);
|
|
2111
2175
|
writeFileSync(clientBarrelPath, clientBarrel.join("\n"), "utf8");
|
|
2112
2176
|
written.push(clientBarrelPath);
|
|
2113
|
-
|
|
2177
|
+
reconcilePubspec(
|
|
2114
2178
|
pkgDir(packages.client),
|
|
2115
2179
|
[
|
|
2116
2180
|
`name: ${packages.client}`,
|
|
@@ -2129,6 +2193,7 @@ function emitDartPackages(
|
|
|
2129
2193
|
` path: ../${packages.types} # co-emitted sibling; replace with a hosted constraint when publishing`,
|
|
2130
2194
|
``,
|
|
2131
2195
|
].join("\n"),
|
|
2196
|
+
{ nomos_client: NOMOS_CLIENT_DEP, nomos_flutter: NOMOS_FLUTTER_DEP },
|
|
2132
2197
|
);
|
|
2133
2198
|
|
|
2134
2199
|
// ── the TEST package (optional) — generated business scenarios, test-only dependency ──
|
|
@@ -2195,7 +2260,7 @@ function emitDartPackages(
|
|
|
2195
2260
|
const testBarrelPath = path.join(pkgDir(packages.test), "lib", `${packages.test}.dart`);
|
|
2196
2261
|
writeFileSync(testBarrelPath, testBarrel.join("\n"), "utf8");
|
|
2197
2262
|
written.push(testBarrelPath);
|
|
2198
|
-
|
|
2263
|
+
reconcilePubspec(
|
|
2199
2264
|
pkgDir(packages.test),
|
|
2200
2265
|
[
|
|
2201
2266
|
`name: ${packages.test}`,
|
|
@@ -2217,6 +2282,7 @@ function emitDartPackages(
|
|
|
2217
2282
|
` - lib/${genRel}/${embeddedDeployName}`,
|
|
2218
2283
|
``,
|
|
2219
2284
|
].join("\n"),
|
|
2285
|
+
{ nomos_client: NOMOS_CLIENT_DEP },
|
|
2220
2286
|
);
|
|
2221
2287
|
}
|
|
2222
2288
|
|
|
@@ -2270,7 +2336,7 @@ function emitDartPackages(
|
|
|
2270
2336
|
const compatBarrelPath = path.join(pkgDir(compatName), "lib", `${compatName}.dart`);
|
|
2271
2337
|
writeFileSync(compatBarrelPath, compatBarrel.join("\n"), "utf8");
|
|
2272
2338
|
written.push(compatBarrelPath);
|
|
2273
|
-
|
|
2339
|
+
reconcilePubspec(
|
|
2274
2340
|
pkgDir(compatName),
|
|
2275
2341
|
[
|
|
2276
2342
|
`name: ${compatName}`,
|
|
@@ -2341,6 +2407,8 @@ async function main(): Promise<void> {
|
|
|
2341
2407
|
`${configValue.application} — compiled Nomos application composition`,
|
|
2342
2408
|
`stage: ${emitted.outputPath}`,
|
|
2343
2409
|
`assets: ${emitted.referencedAssets.length} local content-addressed reference(s)`,
|
|
2410
|
+
`deployment: ${emitted.deploymentPackages.filter((p) => p.placement === "reference").length} serving reference package(s), ` +
|
|
2411
|
+
`${emitted.deploymentPackages.filter((p) => p.placement === "payload").length} deferred payload package(s) — derived from the stage`,
|
|
2344
2412
|
`runtime configuration: USD (the authoring .mjs does not travel)`,
|
|
2345
2413
|
].join("\n"));
|
|
2346
2414
|
return;
|
|
@@ -2897,6 +2965,44 @@ async function main(): Promise<void> {
|
|
|
2897
2965
|
] as const) {
|
|
2898
2966
|
const canonical = (composed as unknown as Record<string, unknown>)[field];
|
|
2899
2967
|
if (!Array.isArray(canonical)) continue;
|
|
2968
|
+
// A package-level language may be declared in a different source module from the
|
|
2969
|
+
// aggregate its generated read indexes (workspaceSharing is the canonical case).
|
|
2970
|
+
// Single-module composition then discovers the read in the language module even though
|
|
2971
|
+
// that layer cannot type it. Move every targeted declaration beside its aggregate schema
|
|
2972
|
+
// before filling genuinely missing package opinions. This changes no composed law: one
|
|
2973
|
+
// declaration id still contributes once; it merely gives every authored USD layer a
|
|
2974
|
+
// self-contained, valid read projection.
|
|
2975
|
+
for (const value of canonical) {
|
|
2976
|
+
if (value === null || typeof value !== "object") continue;
|
|
2977
|
+
const id = (value as { id?: unknown }).id;
|
|
2978
|
+
if (typeof id !== "string") continue;
|
|
2979
|
+
const declarationTarget = field === "queries"
|
|
2980
|
+
? (value as { returns?: unknown }).returns
|
|
2981
|
+
: (["counts", "spatials", "sums", "mins", "maxes", "deriveds", "combineds"] as const)
|
|
2982
|
+
.includes(field as never)
|
|
2983
|
+
? (value as { of?: unknown }).of
|
|
2984
|
+
: undefined;
|
|
2985
|
+
if (typeof declarationTarget !== "string") continue;
|
|
2986
|
+
const ownerIndex = inputs.findIndex((input) =>
|
|
2987
|
+
input.module.aggregates.some((aggregate) => aggregate.id === declarationTarget));
|
|
2988
|
+
if (ownerIndex < 0) continue;
|
|
2989
|
+
for (const [inputIndex, input] of inputs.entries()) {
|
|
2990
|
+
if (inputIndex === ownerIndex) continue;
|
|
2991
|
+
const module = input.module as DomainModule & Record<string, unknown>;
|
|
2992
|
+
const existing = module[field];
|
|
2993
|
+
if (!Array.isArray(existing)) continue;
|
|
2994
|
+
(module as Record<string, unknown>)[field] = existing.filter((candidate) =>
|
|
2995
|
+
candidate === null || typeof candidate !== "object" ||
|
|
2996
|
+
(candidate as { id?: unknown }).id !== id);
|
|
2997
|
+
}
|
|
2998
|
+
const owner = inputs[ownerIndex]!.module as DomainModule & Record<string, unknown>;
|
|
2999
|
+
const ownerValues = Array.isArray(owner[field]) ? owner[field] as unknown[] : [];
|
|
3000
|
+
if (!ownerValues.some((candidate) =>
|
|
3001
|
+
candidate !== null && typeof candidate === "object" &&
|
|
3002
|
+
(candidate as { id?: unknown }).id === id)) {
|
|
3003
|
+
(owner as Record<string, unknown>)[field] = [...ownerValues, value];
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
2900
3006
|
const present = new Set(inputs.flatMap((input) => {
|
|
2901
3007
|
const values = (input.module as unknown as Record<string, unknown>)[field];
|
|
2902
3008
|
return Array.isArray(values)
|
|
@@ -2956,7 +3062,11 @@ async function main(): Promise<void> {
|
|
|
2956
3062
|
? (JSON.parse(readFileSync(lockPath, "utf8")) as StableIdLock)
|
|
2957
3063
|
: undefined;
|
|
2958
3064
|
const inferredRenames: { domain: string; renames: InferredRename[] }[] = [];
|
|
2959
|
-
|
|
3065
|
+
// The lock is project-wide, not package-local. Several package configs commonly live beside one
|
|
3066
|
+
// application stage; compiling one must not erase the stable identity of domains owned by its siblings.
|
|
3067
|
+
// Current package domains are replaced below, while absent domains remain only as continuity metadata
|
|
3068
|
+
// and never leak into this package's interface, law, clients or deployment.
|
|
3069
|
+
const nextLock: StableIdLock = { v: 1, domains: { ...(priorLock?.domains ?? {}) } };
|
|
2960
3070
|
for (const mod of domainModules) {
|
|
2961
3071
|
const identityName = mod.name;
|
|
2962
3072
|
// The CURRENT canonical aggregate schemas (a pre-continuity manifest pass —
|
|
@@ -3052,7 +3162,9 @@ async function main(): Promise<void> {
|
|
|
3052
3162
|
};
|
|
3053
3163
|
}
|
|
3054
3164
|
// Persist the lockfile (commit it with the law: it is how a rename keeps its
|
|
3055
|
-
// identity across clean clones).
|
|
3165
|
+
// identity across clean clones). Existing project-domain order is retained, so once sibling
|
|
3166
|
+
// packages are registered, recompiling them in any order rewrites the same bytes without a
|
|
3167
|
+
// giant formatting-only diff in an established lock.
|
|
3056
3168
|
writeFileSync(lockPath, JSON.stringify(nextLock, null, 2) + "\n", "utf8");
|
|
3057
3169
|
|
|
3058
3170
|
// ── THE STRUCTURAL INTERFACE (#72) — directives (payload shapes) + aggregates (read
|
|
@@ -3434,24 +3546,21 @@ async function main(): Promise<void> {
|
|
|
3434
3546
|
for (const ld of layeredDomains) {
|
|
3435
3547
|
const authored = emitLayeredDomain(ld.key, ld.inputs, { applyBusinessCorrectionLayer: false });
|
|
3436
3548
|
const canonicalLayer = canonicalLayers.get(`/Nomos/${ld.key}`);
|
|
3437
|
-
//
|
|
3438
|
-
//
|
|
3439
|
-
//
|
|
3440
|
-
//
|
|
3441
|
-
//
|
|
3442
|
-
//
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
const
|
|
3446
|
-
if (!needsPackagePrelude) return emitted;
|
|
3549
|
+
// Authored layers carry structural declarations (aggregate/directive/query prims).
|
|
3550
|
+
// Domain-level `nomos:*` values are the compiler's closure over the complete package
|
|
3551
|
+
// (relation namespaces, read projections, invariant gates, contracts and language
|
|
3552
|
+
// consequences), so placing partial versions on source layers lets weaker generated
|
|
3553
|
+
// facts show through. Strip those derived attributes from source layers and append one
|
|
3554
|
+
// explicit strongest closure layer. This is ordinary OpenUSD composition: source
|
|
3555
|
+
// opinions remain inspectable and the compiler-owned overlay is the one canonical law.
|
|
3556
|
+
const structuralLayers = authored.map((emitted, index) => {
|
|
3557
|
+
const { path: layerPath, prims, queries, deriveds, combineds } = emitted.layer;
|
|
3447
3558
|
const layer: UsdLayer = {
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
...(
|
|
3453
|
-
? { nomosContracts: canonicalLayer.nomosContracts }
|
|
3454
|
-
: {}),
|
|
3559
|
+
path: layerPath,
|
|
3560
|
+
prims,
|
|
3561
|
+
...(queries !== undefined ? { queries } : {}),
|
|
3562
|
+
...(deriveds !== undefined ? { deriveds } : {}),
|
|
3563
|
+
...(combineds !== undefined ? { combineds } : {}),
|
|
3455
3564
|
};
|
|
3456
3565
|
return {
|
|
3457
3566
|
...emitted,
|
|
@@ -3459,6 +3568,20 @@ async function main(): Promise<void> {
|
|
|
3459
3568
|
usda: usdaLayerText(ld.key, emitted.name, layer, ld.inputs[index]!.module, new Set()),
|
|
3460
3569
|
};
|
|
3461
3570
|
});
|
|
3571
|
+
const closureLayers = canonicalLayer === undefined
|
|
3572
|
+
? []
|
|
3573
|
+
: (() => {
|
|
3574
|
+
const layer: UsdLayer = canonicalLayer;
|
|
3575
|
+
const name = "__nomos_package_closure";
|
|
3576
|
+
const canonicalModule = domainModules.find((module) =>
|
|
3577
|
+
(module.domain ?? module.name) === ld.key);
|
|
3578
|
+
return [{
|
|
3579
|
+
name,
|
|
3580
|
+
layer,
|
|
3581
|
+
usda: usdaLayerText(ld.key, name, layer, canonicalModule, new Set()),
|
|
3582
|
+
}];
|
|
3583
|
+
})();
|
|
3584
|
+
const emittedLayers = [...structuralLayers, ...closureLayers];
|
|
3462
3585
|
for (const emitted of emittedLayers) {
|
|
3463
3586
|
const base = `${safe(ld.key)}--${safe(emitted.name)}`;
|
|
3464
3587
|
pendingWrites.push({ path: path.join(layersDir, `${base}.usda`), text: emitted.usda });
|
|
@@ -3485,7 +3608,7 @@ async function main(): Promise<void> {
|
|
|
3485
3608
|
}
|
|
3486
3609
|
for (const w of pendingWrites) writeFileSync(w.path, w.text, "utf8");
|
|
3487
3610
|
layeredRootPath = path.join(outDir, `${cfg.name}.layered.usda`);
|
|
3488
|
-
writeFileSync(layeredRootPath, layeredRootUsda(relPaths
|
|
3611
|
+
writeFileSync(layeredRootPath, layeredRootUsda(relPaths), "utf8");
|
|
3489
3612
|
layeredFileCount = relPaths.length;
|
|
3490
3613
|
|
|
3491
3614
|
const usdcat = resolveUsdcat();
|
|
@@ -3634,6 +3757,9 @@ async function main(): Promise<void> {
|
|
|
3634
3757
|
// agent who simply cats the build output.
|
|
3635
3758
|
const summary = {
|
|
3636
3759
|
package: cfg.name,
|
|
3760
|
+
// Binds the deploy body to the published package asset consumed by an application stage. This is the
|
|
3761
|
+
// content identity of the composed source layers; domainHash below is the runtime package-law hash.
|
|
3762
|
+
sourceIdentity: composedUsdIdentity!,
|
|
3637
3763
|
domainHash,
|
|
3638
3764
|
domains: presentationDomainModules.map((m) => ({
|
|
3639
3765
|
domain: m.domain ?? m.name,
|
package/src/framework/home.ts
CHANGED
|
@@ -81,7 +81,11 @@ export const ReceivingGroup = aggregate("NomosInternalReceivingGroup7c1f5e", {
|
|
|
81
81
|
workspaceName: t.string().merge(Lww),
|
|
82
82
|
/** ISO-8601, caller-stamped (determinism — no clock in the plan). */
|
|
83
83
|
bornAt: t.string().merge(Lww),
|
|
84
|
-
|
|
84
|
+
// The group's genesis seats its controller in OWNED_READ_RELATION below. Declaring that same capability
|
|
85
|
+
// here makes the law's coarse pack gate recognise the seat, so a newly enrolled device can clone its own
|
|
86
|
+
// receiving group before any tenant records or tenant-authored grants exist. This remains ordinary,
|
|
87
|
+
// replayable Zanzibar state: revocation removes the seat; there is no kernel-level founder bypass.
|
|
88
|
+
}, { cap: 1 }).readableBy(OWNED_READ_RELATION);
|
|
85
89
|
|
|
86
90
|
/**
|
|
87
91
|
* The framework-owned device lifecycle policy for one receiving group. This is deliberately data, not a
|
|
@@ -177,6 +181,7 @@ export const seedReceivingGroup = directive("nomosInternalSeedReceivingGroup7c1f
|
|
|
177
181
|
export const receivingGroupNameBindsPrincipal = workspaceInvariant("ReceivingGroupNameBindsPrincipal")
|
|
178
182
|
.on("nomosInternalSeedReceivingGroup7c1f5e")
|
|
179
183
|
.reads(() => [refAs("group", "NomosInternalReceivingGroup7c1f5e", RECEIVING_GROUP_ID)])
|
|
184
|
+
.declassify("the receiving-group address is a public hash of its stable principal; refusing a mismatch reveals no private field")
|
|
180
185
|
.assert(({ group }) => {
|
|
181
186
|
const g = (group ?? {}) as { principal?: string; workspaceName?: string };
|
|
182
187
|
const expected = RECEIVING_GROUP_NAME_PREFIX
|
|
@@ -196,6 +201,7 @@ const receivingGroupPolicyRefs = () => [
|
|
|
196
201
|
export const receivingGroupRejectsKeylessAdditionalDevice = workspaceInvariant("ReceivingGroupRejectsKeylessAdditionalDevice")
|
|
197
202
|
.on("enrollFirstDevice")
|
|
198
203
|
.reads(receivingGroupPolicyRefs)
|
|
204
|
+
.declassify("device enrollment deliberately reveals that this receiving group has already completed first-device bootstrap")
|
|
199
205
|
.assert(({ group }) => typeof group?.principal !== "string" || group.principal.length === 0
|
|
200
206
|
? { accept: true }
|
|
201
207
|
: { reject: "receiving-group-additional-device-requires-current-device-or-guardian" });
|
package/src/law_check.ts
CHANGED
|
@@ -409,6 +409,14 @@ function unreadPayloadFindings(domain: string, dir: Directive<unknown>, probe: P
|
|
|
409
409
|
// the gate object from a named payload id field (per-record authz). Excluding it keeps a real
|
|
410
410
|
// gate-consumed field from being flagged dead (the todo-pro `shareTodo.listId` case).
|
|
411
411
|
const gateConsumed = new Set<string>();
|
|
412
|
+
// Compiler-owned law may deliberately consume proof material only in an invariant. The
|
|
413
|
+
// plan must not persist a signature merely to satisfy this lint, and the tenant must not
|
|
414
|
+
// receive a warning about private security machinery. This is internal lowering metadata,
|
|
415
|
+
// not an authored escape hatch.
|
|
416
|
+
for (const field of (dir as { nomosInvariantConsumedPayloadFields?: readonly string[] })
|
|
417
|
+
.nomosInvariantConsumedPayloadFields ?? []) {
|
|
418
|
+
gateConsumed.add(field);
|
|
419
|
+
}
|
|
412
420
|
const objectFromField = (dir as { requiresRelationGate?: { objectFrom?: { idField?: string } } })
|
|
413
421
|
.requiresRelationGate?.objectFrom?.idField;
|
|
414
422
|
if (typeof objectFromField === "string") gateConsumed.add(objectFromField);
|
package/src/usd_layers.ts
CHANGED
|
@@ -83,6 +83,11 @@ export const NOMOS_USD_LAYERED_PACKAGE_FORMAT = "nomos.usd-layered-package.v1";
|
|
|
83
83
|
/** The self-contained ledger/runtime stage: native law plus indexed executable plan assets. */
|
|
84
84
|
export const NOMOS_USD_PLAN_PACKAGE_FORMAT = "nomos.openusd-plan-package.v2";
|
|
85
85
|
|
|
86
|
+
/** Append generated lines without turning a large law into function arguments. */
|
|
87
|
+
function appendLines(target: string[], lines: readonly string[]): void {
|
|
88
|
+
for (const line of lines) target.push(line);
|
|
89
|
+
}
|
|
90
|
+
|
|
86
91
|
// ── small codecs ───────────────────────────────────────────────────────────────────
|
|
87
92
|
|
|
88
93
|
/** Escape a string for a double-quoted usda string literal. */
|
|
@@ -201,7 +206,8 @@ export function nativeValueLines(name: string, value: unknown, indent: string):
|
|
|
201
206
|
const position = `i${String(index).padStart(6, "0")}`;
|
|
202
207
|
const identity = key === undefined ? position : `${position}:k${keyHex(key)}`;
|
|
203
208
|
if (child !== null && typeof child === "object") {
|
|
204
|
-
lines.push(""
|
|
209
|
+
lines.push("");
|
|
210
|
+
appendLines(lines, emitContainer(identity.replace(/:/g, "_"), child, `${at} `));
|
|
205
211
|
} else {
|
|
206
212
|
lines.push(scalarLine(child, identity, `${at} `));
|
|
207
213
|
}
|
|
@@ -210,11 +216,9 @@ export function nativeValueLines(name: string, value: unknown, indent: string):
|
|
|
210
216
|
return lines;
|
|
211
217
|
};
|
|
212
218
|
const rootName = `__nms_${primName(name)}_${hash}`;
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
...emitContainer(rootName, value, indent),
|
|
217
|
-
];
|
|
219
|
+
const lines = [`${indent}custom string nomos:value:${name} = ${usdaString(hash)}`, ""];
|
|
220
|
+
appendLines(lines, emitContainer(rootName, value, indent));
|
|
221
|
+
return lines;
|
|
218
222
|
}
|
|
219
223
|
|
|
220
224
|
/** Field→driver map with keys sorted (the canonical schema shape `manifest.ts` emits). */
|
|
@@ -243,7 +247,11 @@ function domainLayerAttrLines(domain: string, layer: UsdLayer, indent: string):
|
|
|
243
247
|
["eraCertGate", layer.nomosEraCertGate], ["valueObjects", layer.nomosValueObjects],
|
|
244
248
|
["contracts", layer.nomosContracts], ["foreignRefs", layer.nomosForeignRefs], ["flowGate", layer.nomosFlowGate],
|
|
245
249
|
];
|
|
246
|
-
for (const [name, value] of values)
|
|
250
|
+
for (const [name, value] of values) {
|
|
251
|
+
if (value === undefined) continue;
|
|
252
|
+
attrs.push("");
|
|
253
|
+
appendLines(attrs, nativeValueLines(name, value, indent));
|
|
254
|
+
}
|
|
247
255
|
return attrs;
|
|
248
256
|
}
|
|
249
257
|
|
|
@@ -379,7 +387,7 @@ export function usdaLayerText(
|
|
|
379
387
|
for (const [field, driver] of Object.entries(prim.schema)) {
|
|
380
388
|
body.push(`${ind3}custom string nomos:driver:${fieldToken(field)} = ${usdaString(driverToken(driver))}`);
|
|
381
389
|
}
|
|
382
|
-
body
|
|
390
|
+
appendLines(body, relationLines(domainScope, aggById.get(prim.type), ind3));
|
|
383
391
|
} else {
|
|
384
392
|
body.push(
|
|
385
393
|
`${ind3}custom string nomos:kind = "directive"`,
|
|
@@ -392,13 +400,16 @@ export function usdaLayerText(
|
|
|
392
400
|
body.push(`${ind3}custom string[] nomos:reads = ${usdaStringArray(prim.reads)}`);
|
|
393
401
|
}
|
|
394
402
|
if (prim.emits !== undefined) {
|
|
395
|
-
body.push(""
|
|
403
|
+
body.push("");
|
|
404
|
+
appendLines(body, nativeValueLines("emits", prim.emits, ind3));
|
|
396
405
|
}
|
|
397
406
|
if (prim.secondaryOutputs !== undefined) {
|
|
398
|
-
body.push(""
|
|
407
|
+
body.push("");
|
|
408
|
+
appendLines(body, nativeValueLines("secondaryOutputs", prim.secondaryOutputs, ind3));
|
|
399
409
|
}
|
|
400
410
|
if (prim.payloadJsonSchema !== undefined) {
|
|
401
|
-
body.push(""
|
|
411
|
+
body.push("");
|
|
412
|
+
appendLines(body, nativeValueLines("payloadJsonSchema", prim.payloadJsonSchema, ind3));
|
|
402
413
|
}
|
|
403
414
|
}
|
|
404
415
|
body.push(`${ind2}}`, ``);
|
|
@@ -424,7 +435,9 @@ export function usdaLayerText(
|
|
|
424
435
|
`${ind3}custom string nomos:id = ${usdaString(d.id)}`,
|
|
425
436
|
`${ind3}custom string nomos:of = ${usdaString(d.of)}`,
|
|
426
437
|
``,
|
|
427
|
-
|
|
438
|
+
);
|
|
439
|
+
appendLines(body, nativeValueLines("returns", d.returns, ind3));
|
|
440
|
+
body.push(
|
|
428
441
|
`${ind2}}`,
|
|
429
442
|
``,
|
|
430
443
|
);
|
|
@@ -439,7 +452,9 @@ export function usdaLayerText(
|
|
|
439
452
|
`${ind3}custom string nomos:refField = ${usdaString(c.refField)}`,
|
|
440
453
|
`${ind3}custom string nomos:readsFrom = ${usdaString(c.reads)}`,
|
|
441
454
|
``,
|
|
442
|
-
|
|
455
|
+
);
|
|
456
|
+
appendLines(body, nativeValueLines("returns", c.returns, ind3));
|
|
457
|
+
body.push(
|
|
443
458
|
`${ind2}}`,
|
|
444
459
|
``,
|
|
445
460
|
);
|
|
@@ -447,7 +462,7 @@ export function usdaLayerText(
|
|
|
447
462
|
// Drop the trailing blank line inside the scope.
|
|
448
463
|
if (body[body.length - 1] === "") body.pop();
|
|
449
464
|
|
|
450
|
-
|
|
465
|
+
const lines = [
|
|
451
466
|
`#usda 1.0`,
|
|
452
467
|
`(`,
|
|
453
468
|
` customLayerData = {`,
|
|
@@ -461,14 +476,17 @@ export function usdaLayerText(
|
|
|
461
476
|
`{`,
|
|
462
477
|
` def Scope ${usdaString(primName(domain))}`,
|
|
463
478
|
` {`,
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
479
|
+
];
|
|
480
|
+
// Stable ids + holon-owned manifests: native layer-level law values.
|
|
481
|
+
appendLines(lines, domainLayerAttrLines(domain, layer, " "));
|
|
482
|
+
lines.push(``);
|
|
483
|
+
appendLines(lines, body);
|
|
484
|
+
lines.push(
|
|
468
485
|
` }`,
|
|
469
486
|
`}`,
|
|
470
487
|
``,
|
|
471
|
-
|
|
488
|
+
);
|
|
489
|
+
return lines.join("\n");
|
|
472
490
|
}
|
|
473
491
|
|
|
474
492
|
/**
|
|
@@ -482,26 +500,18 @@ export function layeredRootUsda(
|
|
|
482
500
|
composed?: UsdDocument,
|
|
483
501
|
): string {
|
|
484
502
|
const strongestFirst = [...sublayerRelPathsWeakToStrong].reverse();
|
|
485
|
-
const composedDomainOpinions =
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
...domainLayerAttrLines(domain, layer, " "),
|
|
498
|
-
` }`,
|
|
499
|
-
];
|
|
500
|
-
}),
|
|
501
|
-
`}`,
|
|
502
|
-
``,
|
|
503
|
-
];
|
|
504
|
-
return [
|
|
503
|
+
const composedDomainOpinions: string[] = [];
|
|
504
|
+
if (composed !== undefined && composed.layers.length > 0) {
|
|
505
|
+
composedDomainOpinions.push(``, `over "Nomos"`, `{`);
|
|
506
|
+
for (const layer of composed.layers) {
|
|
507
|
+
const domain = layer.path.slice(layer.path.lastIndexOf("/") + 1);
|
|
508
|
+
composedDomainOpinions.push(` over ${usdaString(primName(domain))}`, ` {`);
|
|
509
|
+
appendLines(composedDomainOpinions, domainLayerAttrLines(domain, layer, " "));
|
|
510
|
+
composedDomainOpinions.push(` }`);
|
|
511
|
+
}
|
|
512
|
+
composedDomainOpinions.push(`}`, ``);
|
|
513
|
+
}
|
|
514
|
+
const lines = [
|
|
505
515
|
`#usda 1.0`,
|
|
506
516
|
`(`,
|
|
507
517
|
` doc = """Nomos layered domain package. subLayers are STRONGEST-FIRST (USD layer-stack`,
|
|
@@ -512,14 +522,17 @@ export function layeredRootUsda(
|
|
|
512
522
|
` string "nomos:format" = ${usdaString(NOMOS_USD_LAYERED_PACKAGE_FORMAT)}`,
|
|
513
523
|
` }`,
|
|
514
524
|
` subLayers = [`,
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
)
|
|
525
|
+
];
|
|
526
|
+
strongestFirst.forEach((p, index) => {
|
|
527
|
+
lines.push(` @${p}@${index < strongestFirst.length - 1 ? "," : ""}`);
|
|
528
|
+
});
|
|
529
|
+
lines.push(
|
|
518
530
|
` ]`,
|
|
519
531
|
`)`,
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
532
|
+
);
|
|
533
|
+
appendLines(lines, composedDomainOpinions);
|
|
534
|
+
lines.push(``);
|
|
535
|
+
return lines.join("\n");
|
|
523
536
|
}
|
|
524
537
|
|
|
525
538
|
export interface UsdPlanAsset {
|
|
@@ -579,7 +592,8 @@ export function nativePlanPackageUsda(
|
|
|
579
592
|
if (opened && balance === 0) { end = index; break; }
|
|
580
593
|
}
|
|
581
594
|
if (end < 0) throw new Error(`usd-layers: native domain '${domain}' has unbalanced scopes`);
|
|
582
|
-
domainBlocks
|
|
595
|
+
appendLines(domainBlocks, lines.slice(start, end + 1));
|
|
596
|
+
domainBlocks.push("");
|
|
583
597
|
}
|
|
584
598
|
if (domainBlocks[domainBlocks.length - 1] === "") domainBlocks.pop();
|
|
585
599
|
|
|
@@ -617,7 +631,7 @@ export function nativePlanPackageUsda(
|
|
|
617
631
|
if (runtime[runtime.length - 1] === "") runtime.pop();
|
|
618
632
|
runtime.push(` }`, `}`);
|
|
619
633
|
|
|
620
|
-
|
|
634
|
+
const lines = [
|
|
621
635
|
`#usda 1.0`,
|
|
622
636
|
`(`,
|
|
623
637
|
` customLayerData = {`,
|
|
@@ -627,12 +641,15 @@ export function nativePlanPackageUsda(
|
|
|
627
641
|
``,
|
|
628
642
|
`def Scope "Nomos"`,
|
|
629
643
|
`{`,
|
|
630
|
-
|
|
644
|
+
];
|
|
645
|
+
appendLines(lines, domainBlocks);
|
|
646
|
+
lines.push(
|
|
631
647
|
`}`,
|
|
632
648
|
``,
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
649
|
+
);
|
|
650
|
+
appendLines(lines, runtime);
|
|
651
|
+
lines.push(``);
|
|
652
|
+
return lines.join("\n");
|
|
636
653
|
}
|
|
637
654
|
|
|
638
655
|
// ── the ordered opinion-map fold (flatten) ───────────────────────────────────────────
|
package/src/workspace_sharing.ts
CHANGED
|
@@ -166,8 +166,8 @@ export function workspaceSharing(options: WorkspaceSharingOptions): WorkspaceSha
|
|
|
166
166
|
businessBirthInternal: true as const,
|
|
167
167
|
};
|
|
168
168
|
const b64 = z.string().regex(/^[A-Za-z0-9+/]+={0,2}$/, "expected base64");
|
|
169
|
-
const redeem = nomosImplementationDetail(
|
|
170
|
-
directive(redeemId)
|
|
169
|
+
const redeem = nomosImplementationDetail({
|
|
170
|
+
...directive(redeemId)
|
|
171
171
|
.mutates(options.orders)
|
|
172
172
|
.payload(z.object({
|
|
173
173
|
orderId: z.string().min(1),
|
|
@@ -199,7 +199,9 @@ export function workspaceSharing(options: WorkspaceSharingOptions): WorkspaceSha
|
|
|
199
199
|
})
|
|
200
200
|
.alsoEnsures(RelationTuple)
|
|
201
201
|
.alsoEnsures(NomosInvitationClaim),
|
|
202
|
-
|
|
202
|
+
// Possession proof is verified by NomosInvitationGate and intentionally never persisted.
|
|
203
|
+
nomosInvariantConsumedPayloadFields: ["invitationSignature"] as const,
|
|
204
|
+
});
|
|
203
205
|
const gate = workspaceInvariant(`NomosInvitationGate:${options.orders.id}`)
|
|
204
206
|
.on(redeemId)
|
|
205
207
|
.reads(({ intent }) => [
|