@githolon/dsl 0.103.1 → 0.103.3
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 +7 -0
- package/dart/lib/src/dev_cloud.dart +5 -0
- package/dart/lib/src/runner/node-entry.mjs +1 -1
- package/dart/lib/src/runner/offline-install.mjs +1 -1
- package/dart/lib/src/test_harness.dart +33 -1
- package/dart/pubspec.lock +1 -1
- package/dart/pubspec.yaml +2 -2
- package/dart/test/first_app_gauntlet_test.dart +16 -1
- package/dart_types/CHANGELOG.md +5 -0
- package/dart_types/pubspec.yaml +1 -1
- package/package.json +1 -1
- package/src/application_home_genesis.ts +73 -0
- package/src/birth_meter.ts +7 -15
- package/src/birth_meter_ensure.ts +9 -0
- package/src/births.ts +18 -0
- package/src/business_birth.ts +44 -1
- package/src/codegen_dart_application.ts +9 -1
- package/src/codegen_dart_scenario.ts +7 -0
- package/src/codegen_proof.ts +11 -1
- package/src/compile_package_main.ts +124 -8
- package/src/directive.ts +8 -0
- package/src/framework/application_home.ts +19 -2
- package/src/framework/birth_meter.ts +4 -5
- package/src/framework/home.ts +71 -5
- package/src/framework/workspaces.ts +44 -57
- package/src/index.ts +7 -1
- package/src/law_graph.ts +23 -0
- package/src/law_graph_usda.ts +11 -1
- package/src/package_config.ts +30 -0
|
@@ -103,7 +103,11 @@ import {
|
|
|
103
103
|
import { synthesizePreconditionInvariants } from "./precondition.js";
|
|
104
104
|
import { BIRTH_FRAMEWORK_QUERIES, synthesizeBirthQuotaInvariants } from "./birth_meter.js";
|
|
105
105
|
import { withBirthMeterExports } from "./birth_meter_ensure.js";
|
|
106
|
-
import {
|
|
106
|
+
import {
|
|
107
|
+
BUSINESS_BIRTH_POLICY_DOMAINS_EXPORT,
|
|
108
|
+
bindBusinessBirthPolicyDomains,
|
|
109
|
+
expandBusinessBirthExports,
|
|
110
|
+
} from "./business_birth.js";
|
|
107
111
|
import { withOwnedCustodyExports } from "./framework/owned_custody.js";
|
|
108
112
|
import { expandRoleExports, rolesOf } from "./role.js";
|
|
109
113
|
import { collectEngineRouting } from "./engine_entry.js";
|
|
@@ -158,6 +162,8 @@ import { checkDomainModules, formatFindings, hasRefusal } from "./law_check.js";
|
|
|
158
162
|
import type { NomosCompat } from "./compat_manifest.js";
|
|
159
163
|
import { requiredKernelFeatures, type FeatureProbeModule } from "./kernel_capabilities.js";
|
|
160
164
|
import { APPLICATION_HOME_DOMAIN } from "./framework/application_home_domain.js";
|
|
165
|
+
import { applicationHomePolicyLaw } from "./framework/application_home.js";
|
|
166
|
+
import { isApplicationHomeGenesis } from "./application_home_genesis.js";
|
|
161
167
|
import { resolveApplicationTopology } from "./application_topology.js";
|
|
162
168
|
import { isBusinessIntent } from "./intent.js";
|
|
163
169
|
import {
|
|
@@ -1599,12 +1605,12 @@ const dartTypesImportFor = (generatedSource: string): string => {
|
|
|
1599
1605
|
* just the floor generated code needs — a lagging pin forces tenants into dependency_overrides. */
|
|
1600
1606
|
// 0.3.0: SurfacedValue<T>/SurfacedObservation/PendingConflictRow — generated Surfaced-field typing +
|
|
1601
1607
|
// the pendingConflicts read rows.
|
|
1602
|
-
const NOMOS_TYPES_DEP = "^0.8.
|
|
1608
|
+
const NOMOS_TYPES_DEP = "^0.8.2";
|
|
1603
1609
|
// 0.48.x: session-bound clients (XClient.bind/NomosClientInterface/DomainClientSessionMismatch/
|
|
1604
1610
|
// DomainNotInstalledForSession, engine.follow, pendingConflicts lanes) — the generated client
|
|
1605
1611
|
// REQUIRES this surface. 0.48.1 fixes NomosSession.watchPendingConflicts delegation.
|
|
1606
|
-
const NOMOS_CLIENT_DEP = "^0.64.
|
|
1607
|
-
const NOMOS_FLUTTER_DEP = "^0.63.
|
|
1612
|
+
const NOMOS_CLIENT_DEP = "^0.64.4";
|
|
1613
|
+
const NOMOS_FLUTTER_DEP = "^0.63.5";
|
|
1608
1614
|
// DRIFT TRIPWIRE: pubspec_pins.test.ts asserts these equal the CURRENT versions in dsl/dart/pubspec.yaml
|
|
1609
1615
|
// and dsl/dart_types/pubspec.yaml — a Dart release without this bump fails the suite, so tenants never
|
|
1610
1616
|
// land back in dependency_overrides (the exact co2 report this closes, third occurrence).
|
|
@@ -1805,6 +1811,10 @@ function generatedPackageApplication(
|
|
|
1805
1811
|
const invitationPolicies = members.flatMap(({ domain, invitationPolicies }) =>
|
|
1806
1812
|
invitationPolicies.map((policy) => ({ domain, ...policy })));
|
|
1807
1813
|
const hasInvitations = invitationPolicies.length > 0;
|
|
1814
|
+
const rootMember = members.find(({ domain }) => domain === homeDomain);
|
|
1815
|
+
if (rootMember === undefined) {
|
|
1816
|
+
throw new Error(`generated Dart package application cannot expose the model for home domain '${homeDomain}'`);
|
|
1817
|
+
}
|
|
1808
1818
|
const duplicate = members.find((candidate, index) =>
|
|
1809
1819
|
members.findIndex((other) => other.member === candidate.member) !== index);
|
|
1810
1820
|
if (duplicate !== undefined) {
|
|
@@ -1817,6 +1827,7 @@ function generatedPackageApplication(
|
|
|
1817
1827
|
["currentUser", "the generated signed-in application participant"],
|
|
1818
1828
|
["currentStatus", "the generated application status"],
|
|
1819
1829
|
["watchStatus", "the generated application status watch"],
|
|
1830
|
+
["model", "the generated model lifecycle"],
|
|
1820
1831
|
["recipient", "the generated typed recipient boundary"],
|
|
1821
1832
|
["createInvitation", "the generated invitation capability"],
|
|
1822
1833
|
["redeemInvitation", "the generated invitation redemption"],
|
|
@@ -1836,7 +1847,7 @@ function generatedPackageApplication(
|
|
|
1836
1847
|
`import 'package:flutter/widgets.dart';`,
|
|
1837
1848
|
`import 'package:nomos_flutter/src/application_status.dart' show ApplicationSignIn, ApplicationStatus;`,
|
|
1838
1849
|
`import 'package:nomos_client/src/nms_generated_runtime.dart' show NmsInternalUsers, NomosSession, User${hasInvitations ? ", Invitation, Recipient" : ""};`,
|
|
1839
|
-
`import 'package:nomos_flutter/src/generated_application_host.dart' show NmsInternalApplicationHost, NmsInternalApplicationUserBinding${hasInvitations ? ", NmsInternalInvitationPolicy" : ""};`,
|
|
1850
|
+
`import 'package:nomos_flutter/src/generated_application_host.dart' show NomosApplicationStartupState, NmsInternalApplicationHost, NmsInternalApplicationUserBinding${hasInvitations ? ", NmsInternalInvitationPolicy" : ""};`,
|
|
1840
1851
|
...members.map(({ file, prefix }) =>
|
|
1841
1852
|
`import '${file}.application.dart' as ${prefix};`),
|
|
1842
1853
|
``,
|
|
@@ -1866,6 +1877,9 @@ function generatedPackageApplication(
|
|
|
1866
1877
|
]),
|
|
1867
1878
|
` const ${application}._({required NomosSession nmsSession, ${hasInvitations ? "required NmsInternalApplicationUserBinding nmsUser, " : ""}required this.currentUser, ${members.map(({ member }) => `required this.${member}`).join(", ")}}) : _nmsSession = nmsSession${hasInvitations ? ", _nmsUser = nmsUser" : ""};`,
|
|
1868
1879
|
``,
|
|
1880
|
+
` /// Prove, adopt or restore the package law against this workspace's exact history.`,
|
|
1881
|
+
` ${rootMember.prefix}.NomosModel get model => ${rootMember.member}.model;`,
|
|
1882
|
+
``,
|
|
1869
1883
|
` /// Current durability and convergence across this business application.`,
|
|
1870
1884
|
` ApplicationStatus get currentStatus => NmsInternalApplicationHost.currentStatus(_nmsSession);`,
|
|
1871
1885
|
``,
|
|
@@ -1916,10 +1930,12 @@ function generatedPackageApplication(
|
|
|
1916
1930
|
` required Widget Function(BuildContext context, ${application} application) builder,`,
|
|
1917
1931
|
` Widget Function(BuildContext context)? loadingBuilder,`,
|
|
1918
1932
|
` Widget Function(BuildContext context, Object error, VoidCallback retry)? errorBuilder,`,
|
|
1933
|
+
` ValueChanged<NomosApplicationStartupState>? onStartupStateChanged,`,
|
|
1919
1934
|
` }) => NmsInternalApplicationHost.home<${application}>(`,
|
|
1920
1935
|
` key: key, cloud: cloud, application: application, signIn: signIn,`,
|
|
1921
1936
|
` bind: nomosInternalBind${application}, builder: builder,`,
|
|
1922
1937
|
` loadingBuilder: loadingBuilder, errorBuilder: errorBuilder,`,
|
|
1938
|
+
` onStartupStateChanged: onStartupStateChanged,`,
|
|
1923
1939
|
` );`,
|
|
1924
1940
|
`}`,
|
|
1925
1941
|
``,
|
|
@@ -2085,6 +2101,7 @@ function emitDartPackages(
|
|
|
2085
2101
|
compat?: NomosCompat,
|
|
2086
2102
|
describePackageName = packages.client,
|
|
2087
2103
|
lawUsdzPath?: string,
|
|
2104
|
+
applicationMaxChildWorkspacesPerUser?: number,
|
|
2088
2105
|
): { written: string[]; cursed: string[] } {
|
|
2089
2106
|
for (const [role, name] of Object.entries(packages)) {
|
|
2090
2107
|
if (typeof name !== "string" || !isPubPackageName(name)) {
|
|
@@ -2547,6 +2564,9 @@ function emitDartPackages(
|
|
|
2547
2564
|
surfaceName,
|
|
2548
2565
|
applicationFile,
|
|
2549
2566
|
applicationModules: perDomain.map(({ dd: candidate }) => candidate.modForDart),
|
|
2567
|
+
...(applicationMaxChildWorkspacesPerUser === undefined
|
|
2568
|
+
? {}
|
|
2569
|
+
: { applicationMaxChildWorkspacesPerUser }),
|
|
2550
2570
|
},
|
|
2551
2571
|
),
|
|
2552
2572
|
"utf8",
|
|
@@ -2765,6 +2785,52 @@ async function main(): Promise<void> {
|
|
|
2765
2785
|
`rename the tenant domain (Nomos will not silently overwrite or reroute it)`,
|
|
2766
2786
|
);
|
|
2767
2787
|
}
|
|
2788
|
+
if (cfg.rootPart !== undefined && cfg.application === undefined) {
|
|
2789
|
+
fail(
|
|
2790
|
+
"published application packages must declare application: { maxChildWorkspacesPerUser }; " +
|
|
2791
|
+
"Nomos will not compile a birth-capable home with an implicit quota",
|
|
2792
|
+
);
|
|
2793
|
+
}
|
|
2794
|
+
// A born application home may carry several co-resident law namespaces. BirthPolicy is namespaced
|
|
2795
|
+
// law-state, so genesis must seed every domain that can itself birth; seeding only the private home
|
|
2796
|
+
// domain would leave (for example) `home/birthEstate` correctly refusing its first offer. Discover the
|
|
2797
|
+
// set from the authored modules and bake it into the private genesis plan. The host never supplies it.
|
|
2798
|
+
const applicationBirthPolicyDomains: string[] = [];
|
|
2799
|
+
let applicationHomeGenesisSource:
|
|
2800
|
+
| { readonly modulePath: string; readonly exportName: string }
|
|
2801
|
+
| undefined;
|
|
2802
|
+
if (cfg.rootPart !== undefined) {
|
|
2803
|
+
applicationBirthPolicyDomains.push(APPLICATION_HOME_DOMAIN);
|
|
2804
|
+
}
|
|
2805
|
+
for (const domain of cfg.domains) {
|
|
2806
|
+
let authored: Mod = {};
|
|
2807
|
+
for (const rel of domain.modules) {
|
|
2808
|
+
const abs = path.resolve(cfgDir, rel);
|
|
2809
|
+
if (!existsSync(abs)) fail(`domain '${domain.key}': module not found: ${abs}`);
|
|
2810
|
+
const imported = await import(pathToFileURL(abs).href) as Mod;
|
|
2811
|
+
authored = { ...authored, ...imported };
|
|
2812
|
+
for (const [exportName, value] of Object.entries(imported)) {
|
|
2813
|
+
if (value !== cfg.application?.homeGenesis || !isApplicationHomeGenesis(value)) continue;
|
|
2814
|
+
if (applicationHomeGenesisSource !== undefined) {
|
|
2815
|
+
fail(
|
|
2816
|
+
"application.homeGenesis is exported from more than one configured law module; " +
|
|
2817
|
+
"keep one canonical declaration",
|
|
2818
|
+
);
|
|
2819
|
+
}
|
|
2820
|
+
applicationHomeGenesisSource = { modulePath: abs, exportName };
|
|
2821
|
+
}
|
|
2822
|
+
}
|
|
2823
|
+
const expanded = expandBusinessBirthExports(authored, domain.key);
|
|
2824
|
+
if (directivesOf(expanded).some((dir) => dir.birthsChild === true)) {
|
|
2825
|
+
applicationBirthPolicyDomains.push(domain.key);
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
if (cfg.application?.homeGenesis !== undefined && applicationHomeGenesisSource === undefined) {
|
|
2829
|
+
fail(
|
|
2830
|
+
"application.homeGenesis must be exported by one configured law module; " +
|
|
2831
|
+
"genesis is custody law, not an inline compiler-only callback",
|
|
2832
|
+
);
|
|
2833
|
+
}
|
|
2768
2834
|
const domainsToCompile: DomainConfig[] = [
|
|
2769
2835
|
...cfg.domains,
|
|
2770
2836
|
...(cfg.rootPart === undefined
|
|
@@ -2944,9 +3010,39 @@ async function main(): Promise<void> {
|
|
|
2944
3010
|
}
|
|
2945
3011
|
}
|
|
2946
3012
|
}
|
|
3013
|
+
if (mods.some((mod) => Object.prototype.hasOwnProperty.call(mod, BUSINESS_BIRTH_POLICY_DOMAINS_EXPORT))) {
|
|
3014
|
+
fail(
|
|
3015
|
+
`domain '${d.key}' exports reserved compiler binding '${BUSINESS_BIRTH_POLICY_DOMAINS_EXPORT}'; ` +
|
|
3016
|
+
"rename the tenant export",
|
|
3017
|
+
);
|
|
3018
|
+
}
|
|
3019
|
+
if (d.key === APPLICATION_HOME_DOMAIN) {
|
|
3020
|
+
const max = cfg.application?.maxChildWorkspacesPerUser;
|
|
3021
|
+
if (max === undefined) {
|
|
3022
|
+
fail("the private application home has no declared maxChildWorkspacesPerUser policy");
|
|
3023
|
+
}
|
|
3024
|
+
const homeGenesis = cfg.application?.homeGenesis;
|
|
3025
|
+
const policyMod = applicationHomePolicyLaw(
|
|
3026
|
+
max,
|
|
3027
|
+
applicationBirthPolicyDomains,
|
|
3028
|
+
homeGenesis,
|
|
3029
|
+
) as Mod;
|
|
3030
|
+
mods.push(policyMod);
|
|
3031
|
+
sourceExprs.push(
|
|
3032
|
+
`() => require(${JSON.stringify(path.join(DSL_DIR, "src", "framework", "application_home.ts"))})` +
|
|
3033
|
+
`.applicationHomePolicyLaw(` +
|
|
3034
|
+
`${JSON.stringify(max)}, ${JSON.stringify(applicationBirthPolicyDomains)}, ` +
|
|
3035
|
+
(applicationHomeGenesisSource === undefined
|
|
3036
|
+
? `undefined)`
|
|
3037
|
+
: `require(${JSON.stringify(applicationHomeGenesisSource.modulePath)})` +
|
|
3038
|
+
`[${JSON.stringify(applicationHomeGenesisSource.exportName)}])`),
|
|
3039
|
+
);
|
|
3040
|
+
}
|
|
2947
3041
|
let merged: Mod = {};
|
|
2948
3042
|
for (const m of mods) merged = { ...merged, ...m };
|
|
2949
|
-
merged =
|
|
3043
|
+
merged[BUSINESS_BIRTH_POLICY_DOMAINS_EXPORT] = applicationBirthPolicyDomains;
|
|
3044
|
+
bindBusinessBirthPolicyDomains(merged, applicationBirthPolicyDomains);
|
|
3045
|
+
merged = expandBusinessBirthExports(merged, d.key, applicationBirthPolicyDomains);
|
|
2950
3046
|
merged = withOwnedCustodyExports(merged);
|
|
2951
3047
|
// `capability()` bundles expand into discoverable entries (the CLI plane's
|
|
2952
3048
|
// merge point) — queries/sums/sid-minting/manifests all see the pieces.
|
|
@@ -3055,6 +3151,10 @@ async function main(): Promise<void> {
|
|
|
3055
3151
|
}
|
|
3056
3152
|
}
|
|
3057
3153
|
|
|
3154
|
+
sourceExprs.push(
|
|
3155
|
+
`() => ({ ${JSON.stringify(BUSINESS_BIRTH_POLICY_DOMAINS_EXPORT)}: ` +
|
|
3156
|
+
`${JSON.stringify(applicationBirthPolicyDomains)} })`,
|
|
3157
|
+
);
|
|
3058
3158
|
const domainSource = ` ${JSON.stringify(d.key)}: [${sourceExprs.join(", ")}],`;
|
|
3059
3159
|
entryDomainSourceByKey.set(d.key, domainSource);
|
|
3060
3160
|
entryGeneratedSourceExprsByKey.set(d.key, sourceExprs.slice(d.modules.length));
|
|
@@ -3562,6 +3662,15 @@ async function main(): Promise<void> {
|
|
|
3562
3662
|
// clients/interfaces; using the pre-closure executable module here published a USDA-only `birth` route.
|
|
3563
3663
|
const graphModule = closedDomainModulesByName.get(mod.name) ?? mod;
|
|
3564
3664
|
const discoveredGraph = discoverLawGraph(graphModule, continuity, nextIdentity);
|
|
3665
|
+
for (const node of discoveredGraph.graph.nodes) {
|
|
3666
|
+
if (node.kind === "intent" && node.births && node.birthPolicySeed === undefined) {
|
|
3667
|
+
fail(
|
|
3668
|
+
`domain '${identityName}' birth intent '${node.entity.label}' has no declared birth-policy ` +
|
|
3669
|
+
"genesis verb; Nomos will not compile a birth-capable law whose children can enter " +
|
|
3670
|
+
"custody without explicit policy",
|
|
3671
|
+
);
|
|
3672
|
+
}
|
|
3673
|
+
}
|
|
3565
3674
|
nextIdentity = discoveredGraph.registry;
|
|
3566
3675
|
nextLock.identity = nextIdentity;
|
|
3567
3676
|
typedLawGraphs.set(identityName, discoveredGraph.graph);
|
|
@@ -4864,7 +4973,14 @@ async function main(): Promise<void> {
|
|
|
4864
4973
|
let birthWarnings: string[] = [];
|
|
4865
4974
|
let proofNotes: string[] = [];
|
|
4866
4975
|
try {
|
|
4867
|
-
const proofSrc = generateTsProof(presentationDomainModules, {
|
|
4976
|
+
const proofSrc = generateTsProof(presentationDomainModules, {
|
|
4977
|
+
packageName: cfg.name,
|
|
4978
|
+
domainHash,
|
|
4979
|
+
interface: packageInterface,
|
|
4980
|
+
...(cfg.application?.maxChildWorkspacesPerUser === undefined
|
|
4981
|
+
? {}
|
|
4982
|
+
: { applicationMaxChildWorkspacesPerUser: cfg.application.maxChildWorkspacesPerUser }),
|
|
4983
|
+
});
|
|
4868
4984
|
proofPath = path.join(outDir, `${cfg.name}.proof.mts`);
|
|
4869
4985
|
writeFileSync(proofPath, proofSrc, "utf8");
|
|
4870
4986
|
// THE PER-DOMAIN LEGS INDEX: every domain with a creating write, so
|
|
@@ -5269,7 +5385,7 @@ async function main(): Promise<void> {
|
|
|
5269
5385
|
if (dartPackages !== undefined) {
|
|
5270
5386
|
const r = emitDartPackages(
|
|
5271
5387
|
presentationDomainModules, dartOut, domainHash, packageInterface, dartPackages,
|
|
5272
|
-
dartNames, nomosCompat, cfg.name, lawUsdzPath,
|
|
5388
|
+
dartNames, nomosCompat, cfg.name, lawUsdzPath, cfg.application?.maxChildWorkspacesPerUser,
|
|
5273
5389
|
);
|
|
5274
5390
|
dartFiles = r.written;
|
|
5275
5391
|
dartCursedHints = r.cursed;
|
package/src/directive.ts
CHANGED
|
@@ -447,6 +447,14 @@ export interface Directive<P = unknown> {
|
|
|
447
447
|
* `attestationAuthorizedGate`/`.attestationAuthorized()` naming convention; the declaration is the noun.)
|
|
448
448
|
*/
|
|
449
449
|
readonly birthsChild?: boolean;
|
|
450
|
+
/** Additional ordinary offers which the born workspace's typed law requires in genesis.
|
|
451
|
+
* This is compiler/runtime metadata attached only to a birthing directive. It contains no
|
|
452
|
+
* payload or fallback value: the kernel checks that the re-derived recipe invokes each named
|
|
453
|
+
* law verb exactly once after installation. */
|
|
454
|
+
readonly birthGenesisRequirements?: readonly {
|
|
455
|
+
readonly domain: string;
|
|
456
|
+
readonly directiveId: string;
|
|
457
|
+
}[];
|
|
450
458
|
/**
|
|
451
459
|
* PROOF-TOOLING PRODUCER MARKER (relation-seed leg). Declares that this directive ESTABLISHES the named
|
|
452
460
|
* relation(s) — i.e. its `plan` folds a `writeTuple` that grants the relation. Purely a static hint for
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
// routable through the runtime OpenAPI, while TS/Dart/OpenAPI business surfaces omit it.
|
|
10
10
|
|
|
11
11
|
import type { AggregateHandle } from "../aggregate.js";
|
|
12
|
+
import type { ApplicationHomeGenesis } from "../application_home_genesis.js";
|
|
12
13
|
import type { Directive } from "../directive.js";
|
|
13
14
|
import type { QueryDecl } from "../query.js";
|
|
14
15
|
import { APPLICATION_HOME_DOMAIN } from "./application_home_domain.js";
|
|
@@ -70,8 +71,24 @@ const aggregate = (value: AggregateHandle): Internal<AggregateHandle> => interna
|
|
|
70
71
|
const directive = <P>(value: Directive<P>): Internal<Directive<P>> => internal(value);
|
|
71
72
|
const query = (value: QueryDecl): Internal<QueryDecl> => internal(value);
|
|
72
73
|
|
|
73
|
-
// Parent primitive + the law its born home uses.
|
|
74
|
-
|
|
74
|
+
// Parent primitive + the law its born home uses. The compiler supplies the application's explicit
|
|
75
|
+
// allowance through this factory; there is deliberately no static/default route to discover.
|
|
76
|
+
export function applicationHomePolicyLaw(
|
|
77
|
+
maxChildWorkspacesPerUser: number,
|
|
78
|
+
birthPolicyDomains: readonly string[],
|
|
79
|
+
homeGenesis?: ApplicationHomeGenesis,
|
|
80
|
+
) {
|
|
81
|
+
return {
|
|
82
|
+
__nomos_birthReceivingGroup: directive(
|
|
83
|
+
birthReceivingGroupForDomain(
|
|
84
|
+
APPLICATION_HOME_DOMAIN,
|
|
85
|
+
maxChildWorkspacesPerUser,
|
|
86
|
+
birthPolicyDomains,
|
|
87
|
+
homeGenesis,
|
|
88
|
+
),
|
|
89
|
+
),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
75
92
|
export const __nomos_ReceivingGroup = aggregate(ReceivingGroup);
|
|
76
93
|
export const __nomos_seedReceivingGroup = directive(seedReceivingGroup);
|
|
77
94
|
export const __nomos_ReceivingGroupPolicy = aggregate(ReceivingGroupPolicy);
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* The meter's STRUCTURE (record + queries + quota invariant + auto-capture) is auto-ensured on every
|
|
10
10
|
* `.births()` law — see `../birth_meter.ts`. THIS module is the part a tenant COMPOSES IN when they
|
|
11
|
-
*
|
|
11
|
+
* uses to govern its required, explicitly seeded allowance:
|
|
12
12
|
*
|
|
13
13
|
* - {@link setBirthPolicy} — set/amend the workspace's default allowance (admin-gated).
|
|
14
14
|
* - {@link grantBirthAllowance} — a per-subject override (admin-gated; latest grant wins).
|
|
@@ -56,7 +56,6 @@ export {
|
|
|
56
56
|
childLifecyclesByStatus,
|
|
57
57
|
birthPolicy,
|
|
58
58
|
birthGrantByPrincipal,
|
|
59
|
-
DEFAULT_MAX_BIRTHS_PER_PRINCIPAL,
|
|
60
59
|
birthQuotaVerdict,
|
|
61
60
|
birthQuotaInvariantId,
|
|
62
61
|
} from "../birth_meter.js";
|
|
@@ -238,7 +237,7 @@ export const setBirthPolicy = directive("setBirthPolicy")
|
|
|
238
237
|
.ensures(BirthPolicy)
|
|
239
238
|
.payload(
|
|
240
239
|
z.object({
|
|
241
|
-
maxBirthsPerPrincipal: z.number().int().
|
|
240
|
+
maxBirthsPerPrincipal: z.number().int().nonnegative(),
|
|
242
241
|
setBy: z.string().min(1),
|
|
243
242
|
/** Caller-stamped (the timestamp doctrine). */
|
|
244
243
|
setAt: z.string().min(1),
|
|
@@ -257,7 +256,7 @@ export const seedBirthPolicy = directive("seedBirthPolicy")
|
|
|
257
256
|
.ensures(BirthPolicy)
|
|
258
257
|
.payload(
|
|
259
258
|
z.object({
|
|
260
|
-
maxBirthsPerPrincipal: z.number().int().
|
|
259
|
+
maxBirthsPerPrincipal: z.number().int().nonnegative(),
|
|
261
260
|
setBy: z.string().min(1),
|
|
262
261
|
setAt: z.string().min(1),
|
|
263
262
|
}),
|
|
@@ -274,7 +273,7 @@ export const grantBirthAllowance = directive("grantBirthAllowance")
|
|
|
274
273
|
.payload(
|
|
275
274
|
z.object({
|
|
276
275
|
principal: z.string().min(1),
|
|
277
|
-
maxBirths: z.number().int().
|
|
276
|
+
maxBirths: z.number().int().nonnegative(),
|
|
278
277
|
grantedBy: z.string().min(1),
|
|
279
278
|
/** epoch-ms — the recency tiebreak (latest grant wins), caller-stamped. */
|
|
280
279
|
grantedAt: z.number().int().nonnegative(),
|
package/src/framework/home.ts
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
* the result, and the owner-authored lane to maintain it.)
|
|
26
26
|
*/
|
|
27
27
|
import { z } from "zod";
|
|
28
|
+
import type { ApplicationHomeGenesis } from "../application_home_genesis.js";
|
|
28
29
|
import { aggregate, instance, OWNED_READ_RELATION } from "../aggregate.js";
|
|
29
30
|
// The device-key-hash wire shape (same validator signing_identity/workspaces use module-locally).
|
|
30
31
|
const sha256Hex = z.string().regex(/^[0-9a-f]{64}$/, "expected lowercase sha256 hex (the owner's device pubkey hash)");
|
|
@@ -268,8 +269,8 @@ export function receivingGroupNameOf(principal: string): string {
|
|
|
268
269
|
* so HomeOwner is seeded and `homeNameBindsOwner` passes (a birth without it would be an invalid, un-owned
|
|
269
270
|
* home).
|
|
270
271
|
*/
|
|
271
|
-
//
|
|
272
|
-
//
|
|
272
|
+
// Standalone framework law explicitly declares `home`; compiler-composed business applications explicitly
|
|
273
|
+
// declare the obscure APPLICATION_HOME_DOMAIN. The kernel refuses law with no attestation-seed registrar.
|
|
273
274
|
export const RECEIVING_GROUP_LAW_DOMAIN = "home";
|
|
274
275
|
/**
|
|
275
276
|
* Build the private home-birth primitive for the domain key that carries the home law.
|
|
@@ -279,11 +280,33 @@ export const RECEIVING_GROUP_LAW_DOMAIN = "home";
|
|
|
279
280
|
* tenant remains free to model a business concept called "home". The key is closed over
|
|
280
281
|
* here and written into every child-genesis step; it is never a tenant payload field.
|
|
281
282
|
*/
|
|
282
|
-
export function birthReceivingGroupForDomain(
|
|
283
|
+
export function birthReceivingGroupForDomain(
|
|
284
|
+
groupLawDomain: string,
|
|
285
|
+
maxChildWorkspacesPerUser: number,
|
|
286
|
+
birthPolicyDomains: readonly string[],
|
|
287
|
+
homeGenesis?: ApplicationHomeGenesis,
|
|
288
|
+
) {
|
|
283
289
|
if (groupLawDomain.length === 0) {
|
|
284
290
|
throw new Error("birthReceivingGroupForDomain requires a non-empty receiving-group law domain");
|
|
285
291
|
}
|
|
286
|
-
|
|
292
|
+
if (!Number.isInteger(maxChildWorkspacesPerUser) || maxChildWorkspacesPerUser < 0) {
|
|
293
|
+
throw new Error(
|
|
294
|
+
"birthReceivingGroupForDomain requires an explicit non-negative maxChildWorkspacesPerUser; " +
|
|
295
|
+
"Nomos has no compiled birth-policy fallback",
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
const policyDomains = [...new Set(birthPolicyDomains)];
|
|
299
|
+
if (
|
|
300
|
+
policyDomains.length === 0 ||
|
|
301
|
+
policyDomains.some((domain) => domain.trim().length === 0) ||
|
|
302
|
+
!policyDomains.includes(groupLawDomain)
|
|
303
|
+
) {
|
|
304
|
+
throw new Error(
|
|
305
|
+
"birthReceivingGroupForDomain requires every co-resident birth-capable law domain, " +
|
|
306
|
+
"including its own domain; Nomos will not birth partially configured law",
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
const birthDirective = directive("nomosInternalBirthReceivingGroup7c1f5e")
|
|
287
310
|
.creates(ReceivingGroup)
|
|
288
311
|
.payload(
|
|
289
312
|
z.object({
|
|
@@ -372,6 +395,32 @@ export function birthReceivingGroupForDomain(groupLawDomain: string) {
|
|
|
372
395
|
actor: founder,
|
|
373
396
|
payload: { principal: p.owner, workspaceName: groupName, bornAt: p.bornAt },
|
|
374
397
|
},
|
|
398
|
+
// The receiving group is born with its child-workspace allowance as explicit law-state. The
|
|
399
|
+
// value is closed over by the package's application policy, so neither the host nor the end
|
|
400
|
+
// user can substitute it in the offer payload.
|
|
401
|
+
...policyDomains.map((domain) => ({
|
|
402
|
+
domain,
|
|
403
|
+
directiveId: "seedBirthPolicy",
|
|
404
|
+
actor: founder,
|
|
405
|
+
payload: {
|
|
406
|
+
maxBirthsPerPrincipal: maxChildWorkspacesPerUser,
|
|
407
|
+
setBy: p.owner,
|
|
408
|
+
setAt: p.bornAt,
|
|
409
|
+
},
|
|
410
|
+
})),
|
|
411
|
+
// Tenant founding law is an ordered list of ordinary offers. Values derive solely from the
|
|
412
|
+
// canonical home facts; every payload is parsed by the target directive before it reaches
|
|
413
|
+
// the child's author gate. There is no host append point and no framework-selected fallback.
|
|
414
|
+
...(homeGenesis?.steps ?? []).map((step) => ({
|
|
415
|
+
domain: step.domain,
|
|
416
|
+
directiveId: step.directive.id,
|
|
417
|
+
actor: founder,
|
|
418
|
+
payload: step.directive.payloadSchema.parse(step.payload({
|
|
419
|
+
owner: p.owner,
|
|
420
|
+
workspaceName: groupName,
|
|
421
|
+
bornAt: p.bornAt,
|
|
422
|
+
})),
|
|
423
|
+
})),
|
|
375
424
|
// ENROL THE OWNER'S DEVICE on the warrant-free prefix (when supplied) — enrollSigner self-binds to
|
|
376
425
|
// ctx.principal() (the owner), so this binds `ownerKeyHash` as the owner's signer BEFORE the warrant
|
|
377
426
|
// closes bootstrap. This is what makes the born home SIGNER-READY (able to sign + self-serve-birth its own
|
|
@@ -418,9 +467,26 @@ export function birthReceivingGroupForDomain(groupLawDomain: string) {
|
|
|
418
467
|
})
|
|
419
468
|
.reads(authProviderQuery) // the parent-side AuthProvider capture the inheritance copies (the #58 lane)
|
|
420
469
|
.births(); // BIRTH IS A DECLARED KERNEL PRIMITIVE — emits the `nomosBirthGate` era key; the kernel folds the recipe
|
|
470
|
+
return {
|
|
471
|
+
...birthDirective,
|
|
472
|
+
...(homeGenesis === undefined || homeGenesis.steps.length === 0
|
|
473
|
+
? {}
|
|
474
|
+
: {
|
|
475
|
+
birthGenesisRequirements: homeGenesis.steps.map((step) => ({
|
|
476
|
+
domain: step.domain,
|
|
477
|
+
directiveId: step.directive.id,
|
|
478
|
+
})),
|
|
479
|
+
}),
|
|
480
|
+
};
|
|
421
481
|
}
|
|
422
482
|
|
|
423
|
-
|
|
483
|
+
/** Build the standalone framework receiving-group law with an explicit allowance. */
|
|
484
|
+
export const birthReceivingGroup = (maxChildWorkspacesPerUser: number) =>
|
|
485
|
+
birthReceivingGroupForDomain(
|
|
486
|
+
RECEIVING_GROUP_LAW_DOMAIN,
|
|
487
|
+
maxChildWorkspacesPerUser,
|
|
488
|
+
[RECEIVING_GROUP_LAW_DOMAIN],
|
|
489
|
+
);
|
|
424
490
|
|
|
425
491
|
/**
|
|
426
492
|
* THE SHARED-WITH-ME INDEX (§5) — one record per cross-custody attestation delivered to this home holon:
|