@githolon/dsl 0.108.4 → 0.108.6
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 +5 -0
- package/dart/lib/src/application_framework_calls.dart +21 -0
- package/dart/lib/src/dev_cloud.dart +67 -0
- package/dart/lib/src/nms_test_environment.dart +1 -1
- package/dart/lib/src/runner/node-entry.mjs +354 -104
- package/dart/lib/src/runner/offline-install.mjs +64 -8
- package/dart/lib/src/session_bound.dart +58 -30
- package/dart/lib/src/test_harness.dart +60 -98
- package/dart/lib/src/webview_bridge.dart +212 -32
- package/dart/pubspec.lock +1 -1
- package/dart/pubspec.yaml +2 -2
- package/dart/test/node_runner_transport_test.dart +5 -5
- package/dart/test/webview_bridge_test.dart +6 -0
- package/dart_types/CHANGELOG.md +4 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pb.dart +428 -2
- package/dart_types/pubspec.yaml +1 -1
- package/package.json +1 -1
- package/runtime/generated-application-host.js +293 -79
- package/src/application_topology.ts +120 -28
- package/src/births.ts +27 -0
- package/src/codegen_call_usda.ts +9 -4
- package/src/codegen_dart_application.ts +146 -37
- package/src/codegen_dart_client.ts +225 -160
- package/src/codegen_dart_framework_calls.ts +89 -4
- package/src/codegen_ts.ts +135 -60
- package/src/compile_package_main.ts +3544 -1460
- package/src/directive.ts +320 -118
- package/src/fields.ts +2 -3
- package/src/framework/application_home.ts +3 -0
- package/src/framework/auth_provider_law.ts +20 -28
- package/src/framework/genesis.ts +32 -1
- package/src/framework/home.ts +45 -8
- package/src/framework/signing_identity.ts +78 -0
- package/src/framework/workspaces.ts +9 -1
- package/src/framework_call_dart_enums.ts +33 -0
- package/src/framework_call_definitions.ts +11 -2
- package/src/index.ts +12 -0
- package/src/intent.ts +525 -194
- package/src/law_graph.ts +32 -4
- package/src/law_graph_usda.ts +3 -0
- package/src/package_config.ts +16 -1
- package/src/read.ts +33 -11
- package/src/wire.ts +1 -0
- package/src/wire_encode.ts +3 -3
|
@@ -87,7 +87,10 @@ import {
|
|
|
87
87
|
isNamespacedAggregate,
|
|
88
88
|
qualifyReadProjectionType,
|
|
89
89
|
} from "./read_projection_namespace.js";
|
|
90
|
-
import {
|
|
90
|
+
import {
|
|
91
|
+
childAuthenticationRecoveryAggregateId,
|
|
92
|
+
isBusinessIntent,
|
|
93
|
+
} from "./intent.js";
|
|
91
94
|
import { publicClientInterface, type DomainInterface } from "./shape_compat.js";
|
|
92
95
|
import { BUSINESS_CORRECTION_DIRECTIVE } from "./framework/business_corrections.js";
|
|
93
96
|
import { deriveDirectiveRecordTarget } from "./workspace_routing.js";
|
|
@@ -114,6 +117,10 @@ export const dartInternalWorkspaceSharingReceiptMethod = (
|
|
|
114
117
|
directiveId: string
|
|
115
118
|
): string => `nmsInternal${cap(camel(directiveId))}`;
|
|
116
119
|
|
|
120
|
+
export const dartInternalChildAuthenticationRecoveryMethod = (
|
|
121
|
+
aggregateId: string,
|
|
122
|
+
): string => `nmsInternalRepair${cap(camel(aggregateId))}Authentication`;
|
|
123
|
+
|
|
117
124
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
118
125
|
type AnyDirective = Directive<any>;
|
|
119
126
|
|
|
@@ -522,11 +529,12 @@ function dartUsdaFieldValue(
|
|
|
522
529
|
): string {
|
|
523
530
|
const selected = `${row}.field(${JSON.stringify(field.lawPath)})`;
|
|
524
531
|
const required = `${row}.requiredField(${JSON.stringify(field.lawPath)})`;
|
|
525
|
-
const foreignWorkspaceExpression =
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
532
|
+
const foreignWorkspaceExpression =
|
|
533
|
+
field.foreignWorkspacePath === undefined
|
|
534
|
+
? undefined
|
|
535
|
+
: `(${row}.field(${JSON.stringify(
|
|
536
|
+
field.foreignWorkspacePath
|
|
537
|
+
)})?.stringValue() ?? '')`;
|
|
530
538
|
const present = dartUsdaPresentValue(
|
|
531
539
|
field.spec,
|
|
532
540
|
field.spec.optional ? `${selected}!` : required,
|
|
@@ -635,14 +643,15 @@ function emitDartUsdaResultDecoder(
|
|
|
635
643
|
const computedFields = decoder.computedFields.map((field) => {
|
|
636
644
|
const selected = `row.field(${JSON.stringify(field.path)})`;
|
|
637
645
|
const required = `row.requiredField(${JSON.stringify(field.path)})`;
|
|
638
|
-
const decode = (value: string) =>
|
|
639
|
-
|
|
640
|
-
|
|
646
|
+
const decode = (value: string) =>
|
|
647
|
+
field.enumType === undefined
|
|
648
|
+
? dartUsdaProjectionValue(field.schema, value)
|
|
649
|
+
: `${field.enumType}.fromWire(${value}.jsonValue() as String)`;
|
|
641
650
|
return field.required
|
|
642
651
|
? ` ${dartMemberName(field.name)}: ${decode(required)},`
|
|
643
652
|
: ` ${dartMemberName(
|
|
644
653
|
field.name
|
|
645
|
-
)}: ${selected} == null ? null : ${decode(`${selected}!`)},`;
|
|
654
|
+
)}: ${selected} == null || ${selected}!.typeName == 'NomosNullValue' ? null : ${decode(`${selected}!`)},`;
|
|
646
655
|
});
|
|
647
656
|
const provenanceFields = decoder.attributedFields.map((name) => {
|
|
648
657
|
const field = decoder.baseFields.find(
|
|
@@ -893,9 +902,10 @@ export function generateDartBridgeClient(
|
|
|
893
902
|
`Dart result USDA codegen: '${domain}.${aggregate.id}' has no canonical USDA path`
|
|
894
903
|
);
|
|
895
904
|
}
|
|
896
|
-
const aggregateEnumDartTypes =
|
|
897
|
-
|
|
898
|
-
|
|
905
|
+
const aggregateEnumDartTypes =
|
|
906
|
+
aggregate.framework === true
|
|
907
|
+
? collectDartEnums([aggregate], [], "nomos_framework").dartTypes
|
|
908
|
+
: enumDartTypes;
|
|
899
909
|
const specs = aggregateFieldSpecs(
|
|
900
910
|
aggregate,
|
|
901
911
|
aggregateEnumDartTypes,
|
|
@@ -967,35 +977,40 @@ export function generateDartBridgeClient(
|
|
|
967
977
|
schema: read.returns,
|
|
968
978
|
required: false,
|
|
969
979
|
})),
|
|
970
|
-
]
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
if (readNode === undefined) {
|
|
975
|
-
throw new Error(
|
|
976
|
-
`Dart result USDA codegen: composed law has no '${domain}.${field.name}' read field`
|
|
980
|
+
]
|
|
981
|
+
.map((field) => {
|
|
982
|
+
const readNode = callUsda.graph.nodes.find(
|
|
983
|
+
(node) => node.kind === "read" && node.entity.label === field.name
|
|
977
984
|
);
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
`Dart result USDA codegen: '${domain}.${field.name}' has no canonical USDA path`
|
|
983
|
-
);
|
|
984
|
-
}
|
|
985
|
-
return { ...field, path };
|
|
986
|
-
}).map((field) => {
|
|
987
|
-
const inner = (() => {
|
|
988
|
-
let schema = field.schema;
|
|
989
|
-
while (["optional", "default", "nullable"].includes(zodKind(schema))) {
|
|
990
|
-
schema = zodDef(schema).innerType!;
|
|
985
|
+
if (readNode === undefined) {
|
|
986
|
+
throw new Error(
|
|
987
|
+
`Dart result USDA codegen: composed law has no '${domain}.${field.name}' read field`
|
|
988
|
+
);
|
|
991
989
|
}
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
990
|
+
const path = callUsda.paths.get(readNode.entity.id);
|
|
991
|
+
if (path === undefined) {
|
|
992
|
+
throw new Error(
|
|
993
|
+
`Dart result USDA codegen: '${domain}.${field.name}' has no canonical USDA path`
|
|
994
|
+
);
|
|
995
|
+
}
|
|
996
|
+
return { ...field, path };
|
|
997
|
+
})
|
|
998
|
+
.map((field) => {
|
|
999
|
+
const inner = (() => {
|
|
1000
|
+
let schema = field.schema;
|
|
1001
|
+
while (
|
|
1002
|
+
["optional", "default", "nullable"].includes(zodKind(schema))
|
|
1003
|
+
) {
|
|
1004
|
+
schema = zodDef(schema).innerType!;
|
|
1005
|
+
}
|
|
1006
|
+
return schema;
|
|
1007
|
+
})();
|
|
1008
|
+
const enumType =
|
|
1009
|
+
field.required && zodKind(inner) === "enum"
|
|
1010
|
+
? enumDartTypes.get(dartEnumLookupKey(inner))
|
|
1011
|
+
: undefined;
|
|
1012
|
+
return enumType === undefined ? field : { ...field, enumType };
|
|
1013
|
+
});
|
|
999
1014
|
const decoder = {
|
|
1000
1015
|
model: readModelClassName(aggregate.id),
|
|
1001
1016
|
refType: concreteClassName(aggregate.id),
|
|
@@ -1055,10 +1070,7 @@ export function generateDartBridgeClient(
|
|
|
1055
1070
|
scopes: readonly { scope: string; owned?: true }[]
|
|
1056
1071
|
): string =>
|
|
1057
1072
|
`[${scopes
|
|
1058
|
-
.map(
|
|
1059
|
-
(s) =>
|
|
1060
|
-
`(scope: ${dartStr(s.scope)}, owned: ${s.owned === true})`
|
|
1061
|
-
)
|
|
1073
|
+
.map((s) => `(scope: ${dartStr(s.scope)}, owned: ${s.owned === true})`)
|
|
1062
1074
|
.join(", ")}]`;
|
|
1063
1075
|
const needsReadPrincipal = (typeId: string): boolean => {
|
|
1064
1076
|
const audience = aggById.get(typeId)?.audience;
|
|
@@ -1184,10 +1196,16 @@ export function generateDartBridgeClient(
|
|
|
1184
1196
|
const dirs = allDirs.filter(
|
|
1185
1197
|
(d) => (d as { birthsChild?: boolean }).birthsChild !== true
|
|
1186
1198
|
);
|
|
1199
|
+
// Only a no-argument enrolled-device intent can be the client's automatic stale-token
|
|
1200
|
+
// recovery hook. Enrolled-device intents with typed inputs are explicit business operations
|
|
1201
|
+
// (for example all-device-loss recovery bound to a named principal) and remain ordinary
|
|
1202
|
+
// generated APIs; their proof posture does not make them automatic lifecycle hooks.
|
|
1187
1203
|
const authenticationRecoveryDirs = dirs.filter(
|
|
1188
1204
|
(directive) =>
|
|
1189
1205
|
isBusinessIntent(directive) &&
|
|
1190
|
-
directive.offerAuthenticationProof === "enrolledDevice"
|
|
1206
|
+
directive.offerAuthenticationProof === "enrolledDevice" &&
|
|
1207
|
+
introspectPayload(directive.payloadSchema as z.ZodTypeAny, enumDartTypes)
|
|
1208
|
+
.length === 0
|
|
1191
1209
|
);
|
|
1192
1210
|
if (authenticationRecoveryDirs.length > 1) {
|
|
1193
1211
|
throw new Error(
|
|
@@ -1196,16 +1214,68 @@ export function generateDartBridgeClient(
|
|
|
1196
1214
|
);
|
|
1197
1215
|
}
|
|
1198
1216
|
const authenticationRecovery = authenticationRecoveryDirs[0];
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1217
|
+
const childAuthenticationRecoveries = dirs.flatMap((directive) => {
|
|
1218
|
+
if (!isBusinessIntent(directive)) return [];
|
|
1219
|
+
const aggregateId = childAuthenticationRecoveryAggregateId(directive);
|
|
1220
|
+
if (aggregateId === undefined) return [];
|
|
1221
|
+
const fields = introspectPayload(
|
|
1222
|
+
directive.payloadSchema as z.ZodTypeAny,
|
|
1223
|
+
enumDartTypes,
|
|
1224
|
+
);
|
|
1225
|
+
if (
|
|
1226
|
+
fields.length !== 2 ||
|
|
1227
|
+
fields.some(
|
|
1228
|
+
(field) =>
|
|
1229
|
+
!["workspace", "user"].includes(field.name) ||
|
|
1230
|
+
field.optional ||
|
|
1231
|
+
field.dartType !== "String",
|
|
1232
|
+
)
|
|
1233
|
+
) {
|
|
1234
|
+
throw new Error(
|
|
1235
|
+
`Dart child-authentication recovery '${domain}/${directive.id}' must accept exactly required string fields { workspace, user }`,
|
|
1236
|
+
);
|
|
1237
|
+
}
|
|
1238
|
+
return [{ aggregateId, directive }];
|
|
1239
|
+
});
|
|
1240
|
+
for (const recovery of childAuthenticationRecoveries) {
|
|
1241
|
+
if (
|
|
1242
|
+
childAuthenticationRecoveries.some(
|
|
1243
|
+
(candidate) =>
|
|
1244
|
+
candidate !== recovery &&
|
|
1245
|
+
candidate.aggregateId === recovery.aggregateId,
|
|
1246
|
+
)
|
|
1247
|
+
) {
|
|
1248
|
+
throw new Error(
|
|
1249
|
+
`Dart client codegen: '${domain}' declares more than one authentication repair action for '${recovery.aggregateId}'`,
|
|
1250
|
+
);
|
|
1251
|
+
}
|
|
1252
|
+
const method = safeMethod(recovery.directive.id);
|
|
1253
|
+
const input = dartActionInputClassName(recovery.directive);
|
|
1254
|
+
methods.push(
|
|
1255
|
+
` /// Generated child-open recovery: inspect the child's kernel posture, then ask this`,
|
|
1256
|
+
` /// authenticated parent law to send its committed signer/provider state to that exact child.`,
|
|
1257
|
+
` Future<void> ${dartInternalChildAuthenticationRecoveryMethod(
|
|
1258
|
+
recovery.aggregateId,
|
|
1259
|
+
)}(String workspace) async {`,
|
|
1260
|
+
` final expectedPrincipal = actor ?? bridge.principal ?? '';`,
|
|
1261
|
+
` if (expectedPrincipal.isEmpty) {`,
|
|
1262
|
+
` throw StateError('Child authentication recovery requires the signed-in principal.');`,
|
|
1263
|
+
` }`,
|
|
1264
|
+
` final child = await bridge.acquireSession(workspace);`,
|
|
1265
|
+
` final posture = await child.nmsAuthenticationPosture(principal: expectedPrincipal);`,
|
|
1266
|
+
` if (posture.isReadyFor(expectedPrincipal)) return;`,
|
|
1267
|
+
` await ${method}(${input}(`,
|
|
1268
|
+
` workspace: workspace, user: expectedPrincipal,`,
|
|
1269
|
+
` ));`,
|
|
1270
|
+
` final parentSync = await bridge.sync(admit: true);`,
|
|
1271
|
+
` if (parentSync.offline || parentSync.pushFailed) {`,
|
|
1272
|
+
` throw StateError('Parent authentication recovery could not reach custody: ' +`,
|
|
1273
|
+
` (parentSync.hasPushError() ? parentSync.pushError.message : 'workspace is offline'));`,
|
|
1274
|
+
` }`,
|
|
1275
|
+
` await child.nmsAwaitProviderTokenReady(principal: expectedPrincipal);`,
|
|
1276
|
+
` await child.nmsAwaitAuthenticationReady(principal: expectedPrincipal);`,
|
|
1277
|
+
` }`,
|
|
1278
|
+
``,
|
|
1209
1279
|
);
|
|
1210
1280
|
}
|
|
1211
1281
|
const localOfferDirs = [...dirs, ...birthDirs];
|
|
@@ -1261,7 +1331,12 @@ export function generateDartBridgeClient(
|
|
|
1261
1331
|
);
|
|
1262
1332
|
const payloadClass = compilerInternal
|
|
1263
1333
|
? `({${specs
|
|
1264
|
-
.map(
|
|
1334
|
+
.map(
|
|
1335
|
+
(spec) =>
|
|
1336
|
+
`${spec.dartType}${spec.optional ? "?" : ""} ${dartMemberName(
|
|
1337
|
+
spec.name
|
|
1338
|
+
)}`
|
|
1339
|
+
)
|
|
1265
1340
|
.join(", ")}})`
|
|
1266
1341
|
: dartActionInputClassName(directive);
|
|
1267
1342
|
if (compilerInternal) internalRecordTypes.set(directive.id, payloadClass);
|
|
@@ -1305,7 +1380,16 @@ export function generateDartBridgeClient(
|
|
|
1305
1380
|
for (const input of dartBusinessRecordInputs(directive, mod.aggregates)) {
|
|
1306
1381
|
const origin = `NmsInternalRecordOrigins.require(payload.${input.argument})`;
|
|
1307
1382
|
access.set(input.idField, { value: `${origin}.id` });
|
|
1308
|
-
access.set(input.workspaceField, {
|
|
1383
|
+
access.set(input.workspaceField, {
|
|
1384
|
+
value: `${origin}.workspace`,
|
|
1385
|
+
// A business record always has a canonical origin. When the authored
|
|
1386
|
+
// wire field is optional, the generated application input replaces it
|
|
1387
|
+
// with that required typed record, so the derived workspace is present
|
|
1388
|
+
// by construction rather than nullable caller data.
|
|
1389
|
+
...(specsByName.get(input.workspaceField)?.optional === true
|
|
1390
|
+
? { present: "true" }
|
|
1391
|
+
: {}),
|
|
1392
|
+
});
|
|
1309
1393
|
}
|
|
1310
1394
|
for (const input of dartForeignReferenceInputs(directive, mod.aggregates)) {
|
|
1311
1395
|
const member = `payload.${dartMemberName(input.idField)}`;
|
|
@@ -1322,8 +1406,8 @@ export function generateDartBridgeClient(
|
|
|
1322
1406
|
...(input.optional
|
|
1323
1407
|
? present
|
|
1324
1408
|
: specsByName.get(input.workspaceField)?.optional === true
|
|
1325
|
-
|
|
1326
|
-
|
|
1409
|
+
? { present: "true" }
|
|
1410
|
+
: {}),
|
|
1327
1411
|
});
|
|
1328
1412
|
}
|
|
1329
1413
|
const stamps = autoStampFields(
|
|
@@ -2491,7 +2575,9 @@ export function generateDartBridgeClient(
|
|
|
2491
2575
|
(spec) =>
|
|
2492
2576
|
spec.name !== b.valuePayloadField &&
|
|
2493
2577
|
spec.name !== b.idPayloadField &&
|
|
2494
|
-
!autoStampFields(dir.payloadSchema as z.ZodTypeAny).includes(
|
|
2578
|
+
!autoStampFields(dir.payloadSchema as z.ZodTypeAny).includes(
|
|
2579
|
+
spec.name
|
|
2580
|
+
)
|
|
2495
2581
|
);
|
|
2496
2582
|
const needsTypedDecision = otherPublicFacts.some(
|
|
2497
2583
|
(spec) => !spec.optional
|
|
@@ -2505,8 +2591,7 @@ export function generateDartBridgeClient(
|
|
|
2505
2591
|
`final class ${decisionClass} {`,
|
|
2506
2592
|
` final ${mergedSpec.dartType} merged;`,
|
|
2507
2593
|
...otherPublicFacts.map(
|
|
2508
|
-
(spec) =>
|
|
2509
|
-
` final ${spec.dartType} ${dartMemberName(spec.name)};`
|
|
2594
|
+
(spec) => ` final ${spec.dartType} ${dartMemberName(spec.name)};`
|
|
2510
2595
|
),
|
|
2511
2596
|
` const ${decisionClass}({required this.merged, ${otherPublicFacts
|
|
2512
2597
|
.map((spec) => `required this.${dartMemberName(spec.name)}`)
|
|
@@ -2532,7 +2617,9 @@ export function generateDartBridgeClient(
|
|
|
2532
2617
|
"payload",
|
|
2533
2618
|
...encoder.arguments,
|
|
2534
2619
|
].join(", ")})`;
|
|
2535
|
-
const mergedExpression = needsTypedDecision
|
|
2620
|
+
const mergedExpression = needsTypedDecision
|
|
2621
|
+
? "decision.merged"
|
|
2622
|
+
: "merged";
|
|
2536
2623
|
const payloadArguments = [
|
|
2537
2624
|
`${dartMemberName(b.valuePayloadField)}: ${mergedExpression}`,
|
|
2538
2625
|
...(needsTypedDecision
|
|
@@ -2922,15 +3009,11 @@ export function generateDartBridgeClient(
|
|
|
2922
3009
|
: aOwned
|
|
2923
3010
|
? `, await _client._readKeys(${aScopesExpr}, [(type: ${dartStr(
|
|
2924
3011
|
runtimeType
|
|
2925
|
-
)}, id: id)])${
|
|
2926
|
-
aPrivate ? ", _client.actor" : ""
|
|
2927
|
-
}`
|
|
3012
|
+
)}, id: id)])${aPrivate ? ", _client.actor" : ""}`
|
|
2928
3013
|
: pastKeysArg(agg.id);
|
|
2929
3014
|
const nominalByIdWatch = aScopes.length
|
|
2930
3015
|
? `Stream.fromFuture(_readKeys(${aScopesExpr}${
|
|
2931
|
-
aOwned
|
|
2932
|
-
? `, [(type: ${dartStr(runtimeType)}, id: id)]`
|
|
2933
|
-
: ""
|
|
3016
|
+
aOwned ? `, [(type: ${dartStr(runtimeType)}, id: id)]` : ""
|
|
2934
3017
|
})).asyncExpand((keys) => bridge.nmsWatchByIdUsda(${dartStr(
|
|
2935
3018
|
runtimeType
|
|
2936
3019
|
)}, id, keys: keys${
|
|
@@ -2996,7 +3079,9 @@ export function generateDartBridgeClient(
|
|
|
2996
3079
|
aOwned
|
|
2997
3080
|
? ` _client._readWithOwnedKeys(${aScopesExpr}, (keys) async => _nmsDecode${model}Rows(await _client.bridge.nmsQueryByTypeAtUsda(moment, ${dartStr(
|
|
2998
3081
|
runtimeType
|
|
2999
|
-
)}, keys${
|
|
3082
|
+
)}, keys${
|
|
3083
|
+
aPrivate ? ", _client.actor" : ""
|
|
3084
|
+
}), _client._ws), (row) => row.ref);`
|
|
3000
3085
|
: ` _nmsDecode${model}Rows(await _client.bridge.nmsQueryByTypeAtUsda(moment, ${dartStr(
|
|
3001
3086
|
runtimeType
|
|
3002
3087
|
)}${pastKeysArg(agg.id)}), _client._ws);`,
|
|
@@ -3033,13 +3118,35 @@ export function generateDartBridgeClient(
|
|
|
3033
3118
|
` );`,
|
|
3034
3119
|
` }`,
|
|
3035
3120
|
``,
|
|
3036
|
-
|
|
3121
|
+
...(authenticationRecovery === undefined
|
|
3122
|
+
? [` Future<NomosModelAdoption> _adoptGeneratedModel([String? because]) async {`]
|
|
3123
|
+
: [
|
|
3124
|
+
` Future<NomosModelAdoption> _adoptGeneratedModel([String? because]) =>`,
|
|
3125
|
+
` _adoptGeneratedModelAttempt(because, false);`,
|
|
3126
|
+
` Future<NomosModelAdoption> _adoptGeneratedModelAttempt(`,
|
|
3127
|
+
` String? because, bool recoveredProviderEpoch) async {`,
|
|
3128
|
+
]),
|
|
3037
3129
|
` final result = await bridge.nomosFrameworkAdoptGeneratedModel(`,
|
|
3038
3130
|
` domain: ${dartStr(
|
|
3039
3131
|
domain
|
|
3040
3132
|
)}, candidateHash: domainHash, because: because);`,
|
|
3041
3133
|
` final review = _nomosModelReview(result);`,
|
|
3042
3134
|
` final stage = result.stage;`,
|
|
3135
|
+
...(authenticationRecovery === undefined
|
|
3136
|
+
? []
|
|
3137
|
+
: [
|
|
3138
|
+
` if (!result.accepted && !recoveredProviderEpoch) {`,
|
|
3139
|
+
` final posture = await bridge.nmsAuthenticationPosture(principal: actor);`,
|
|
3140
|
+
` if (posture.providerToken == NomosProviderTokenVerification.noMatchingKey) {`,
|
|
3141
|
+
` print('[nomos-model-recovery] local ${domain} posture=' + posture.providerToken.name);`,
|
|
3142
|
+
` await ${safeMethod(
|
|
3143
|
+
authenticationRecovery.id
|
|
3144
|
+
)}(const ${dartActionInputClassName(authenticationRecovery)}());`,
|
|
3145
|
+
` await bridge.nmsAwaitAuthenticationReady(principal: actor);`,
|
|
3146
|
+
` return _adoptGeneratedModelAttempt(because, true);`,
|
|
3147
|
+
` }`,
|
|
3148
|
+
` }`,
|
|
3149
|
+
]),
|
|
3043
3150
|
` if ((stage == ModelLifecycleStage.MODEL_LIFECYCLE_STAGE_PROMOTED ||`,
|
|
3044
3151
|
` stage == ModelLifecycleStage.MODEL_LIFECYCLE_STAGE_ALREADY_CURRENT) && result.accepted) {`,
|
|
3045
3152
|
` return NomosModelAdoption.adopted(`,
|
|
@@ -3083,12 +3190,30 @@ export function generateDartBridgeClient(
|
|
|
3083
3190
|
` final archive = await rootBundle.load(${dartStr(
|
|
3084
3191
|
opts.lawAssetKey
|
|
3085
3192
|
)});`,
|
|
3086
|
-
`
|
|
3193
|
+
` Future<void> attempt() => bridge.nomosInternalStageGeneratedModel(`,
|
|
3087
3194
|
` domain: ${dartStr(domain)},`,
|
|
3088
3195
|
` candidateHash: domainHash,`,
|
|
3089
3196
|
` lawUsdz: archive.buffer.asUint8List(`,
|
|
3090
3197
|
` archive.offsetInBytes, archive.lengthInBytes),`,
|
|
3091
3198
|
` );`,
|
|
3199
|
+
...(authenticationRecovery === undefined
|
|
3200
|
+
? [` await attempt();`]
|
|
3201
|
+
: [
|
|
3202
|
+
` try {`,
|
|
3203
|
+
` await attempt();`,
|
|
3204
|
+
` } on NomosModelStageFailure {`,
|
|
3205
|
+
` final posture = await bridge.nmsAuthenticationPosture(principal: actor);`,
|
|
3206
|
+
` print('[nomos-model-recovery] local ${domain} posture=' + posture.providerToken.name);`,
|
|
3207
|
+
` if (posture.providerToken != NomosProviderTokenVerification.noMatchingKey) rethrow;`,
|
|
3208
|
+
` await ${safeMethod(
|
|
3209
|
+
authenticationRecovery.id
|
|
3210
|
+
)}(const ${dartActionInputClassName(
|
|
3211
|
+
authenticationRecovery
|
|
3212
|
+
)}());`,
|
|
3213
|
+
` await bridge.nmsAwaitAuthenticationReady(principal: actor);`,
|
|
3214
|
+
` await attempt();`,
|
|
3215
|
+
` }`,
|
|
3216
|
+
]),
|
|
3092
3217
|
` }`,
|
|
3093
3218
|
``,
|
|
3094
3219
|
]),
|
|
@@ -3788,77 +3913,27 @@ export function generateDartBridgeClient(
|
|
|
3788
3913
|
` /// IMMEDIATELY — at provider construction, not two clicks into the app. The synchronous`,
|
|
3789
3914
|
` /// constructor remains for codepaths that can't await; it lazily runs the SAME preflight on the`,
|
|
3790
3915
|
` /// first dispatch.`,
|
|
3916
|
+
` static Future<void> _catchUpBeforeModelPreparation(NomosSession session) async {`,
|
|
3917
|
+
` try {`,
|
|
3918
|
+
` await session.sync(admit: true);`,
|
|
3919
|
+
` } catch (error) {`,
|
|
3920
|
+
` // Offline-first: inability to reach shared custody does not revoke the local workspace.`,
|
|
3921
|
+
` // The subsequent typed compatibility/model gate remains authoritative and will either`,
|
|
3922
|
+
` // accept the local law or surface the precise governed action still required.`,
|
|
3923
|
+
` print('[nomos-model-catch-up] ${domain} continuing from local custody: ' + error.toString());`,
|
|
3924
|
+
` }`,
|
|
3925
|
+
` }`,
|
|
3926
|
+
``,
|
|
3791
3927
|
` static Future<${className}> bind(NomosSession session, {String? actor${
|
|
3792
3928
|
manualEncryptionPresent ? ", NomosKeyStore? keyStore" : ""
|
|
3793
3929
|
}}) async {`,
|
|
3794
3930
|
` final candidate = ${className}(session, actor: actor${
|
|
3795
3931
|
manualEncryptionPresent ? ", keyStore: keyStore" : ""
|
|
3796
3932
|
});`,
|
|
3797
|
-
`
|
|
3798
|
-
`
|
|
3799
|
-
`
|
|
3800
|
-
`
|
|
3801
|
-
` status: adoption.status.name,`,
|
|
3802
|
-
` summary: adoption.summary,`,
|
|
3803
|
-
` review: DomainModelReview(`,
|
|
3804
|
-
` checkedActions: adoption.review.checkedActions,`,
|
|
3805
|
-
` candidateActions: adoption.review.candidateActions,`,
|
|
3806
|
-
` sameOutcome: adoption.review.sameOutcome,`,
|
|
3807
|
-
` differences: [`,
|
|
3808
|
-
` for (final difference in adoption.review.differences)`,
|
|
3809
|
-
` DomainModelDifference(`,
|
|
3810
|
-
` kind: switch (difference.kind) {`,
|
|
3811
|
-
` NomosModelDifferenceKind.rules => DomainModelDifferenceKind.rules,`,
|
|
3812
|
-
` NomosModelDifferenceKind.outcome => DomainModelDifferenceKind.outcome,`,
|
|
3813
|
-
` NomosModelDifferenceKind.evidence => DomainModelDifferenceKind.evidence,`,
|
|
3814
|
-
` },`,
|
|
3815
|
-
` area: difference.area,`,
|
|
3816
|
-
` action: difference.action,`,
|
|
3817
|
-
` summary: difference.summary,`,
|
|
3818
|
-
` ),`,
|
|
3819
|
-
` ],`,
|
|
3820
|
-
` ),`,
|
|
3821
|
-
...(opts.lawAssetKey === undefined
|
|
3822
|
-
? [` stage: null,`]
|
|
3823
|
-
: [
|
|
3824
|
-
` stage: adoption.status == NomosModelAdoptionStatus.needsStaging`,
|
|
3825
|
-
` ? () => candidate._stageGeneratedModel()`,
|
|
3826
|
-
` : null,`,
|
|
3827
|
-
]),
|
|
3828
|
-
` accept: adoption.status == NomosModelAdoptionStatus.needsDecision`,
|
|
3829
|
-
` ? (reason) async {`,
|
|
3830
|
-
` final accepted = await adoption.accept(reason);`,
|
|
3831
|
-
` // A retry that still isn't adopted (a transient business-outcome/history race,`,
|
|
3832
|
-
` // e.g. concurrent writes landing between the bare preflight and this accept) must`,
|
|
3833
|
-
` // NOT be swallowed as a terminal StateError: re-throw the SAME typed exception so`,
|
|
3834
|
-
` // the caller's governed-steps retry loop gets another decide()/accept() attempt`,
|
|
3835
|
-
` // instead of failing outright on the first race it hits.`,
|
|
3836
|
-
` if (accepted.status != NomosModelAdoptionStatus.adopted) {`,
|
|
3837
|
-
` throwForAdoption(accepted);`,
|
|
3838
|
-
` }`,
|
|
3839
|
-
` }`,
|
|
3840
|
-
` : null,`,
|
|
3841
|
-
` );`,
|
|
3842
|
-
` }`,
|
|
3843
|
-
` Future<void> adoptGeneratedModel() async {`,
|
|
3844
|
-
` final adoption = await candidate._adoptGeneratedModel();`,
|
|
3845
|
-
` if (adoption.status == NomosModelAdoptionStatus.adopted) return;`,
|
|
3846
|
-
` throwForAdoption(adoption);`,
|
|
3847
|
-
` }`,
|
|
3848
|
-
` try {`,
|
|
3849
|
-
` await session.assertCompatible(interface);`,
|
|
3850
|
-
` } on DomainClientSessionMismatch catch (mismatch) {`,
|
|
3851
|
-
` // A deployed successor is deliberately invisible until the generated candidate proves it`,
|
|
3852
|
-
` // against this workspace's exact history. Do that before rejecting the candidate for`,
|
|
3853
|
-
` // expecting its own newer interface; all other bind failures retain their precise cause.`,
|
|
3854
|
-
` if (!mismatch.canAttemptGeneratedModelAdoption) rethrow;`,
|
|
3855
|
-
` await adoptGeneratedModel();`,
|
|
3856
|
-
` }`,
|
|
3857
|
-
` // Shape compatibility governs readable structure; exact package identity governs`,
|
|
3858
|
-
` // which plans may author. A plan-only successor must therefore enter the same`,
|
|
3859
|
-
` // proof-gated lifecycle even when every generated read type remains compatible.`,
|
|
3860
|
-
` final law = await session.lawStatus(interface.expectedDomainHash);`,
|
|
3861
|
-
` if (law['drifted'] == true) await adoptGeneratedModel();`,
|
|
3933
|
+
` // Binding proves that this client can read and author against the law the`,
|
|
3934
|
+
` // workspace already runs. It never deploys, stages, promotes, or rewrites`,
|
|
3935
|
+
` // workspace law. Model rollout is an explicit operator action through`,
|
|
3936
|
+
` // [model], outside application startup.`,
|
|
3862
3937
|
` await session.assertCompatible(interface);`,
|
|
3863
3938
|
` // RELEASE COHERENCE: resolve this law's OSGi Contract Namespace requirements before use.`,
|
|
3864
3939
|
` await session.assertKernelCoherence(kernelContracts, lawHash: ${hashConst});`,
|
|
@@ -3882,38 +3957,26 @@ export function generateDartBridgeClient(
|
|
|
3882
3957
|
` /// privately retains refreshable cloud access for a genuinely-absent authorized workspace.`,
|
|
3883
3958
|
` static Future<${className}> open<TAggregate>(NomosBridge bridge, {NomosRef<TAggregate>? ref, String? workspace, String? actor${
|
|
3884
3959
|
manualEncryptionPresent ? ", NomosKeyStore? keyStore" : ""
|
|
3885
|
-
}}) async {`,
|
|
3960
|
+
}, Future<void> Function()? recoverAuthentication}) async {`,
|
|
3886
3961
|
` final ws = ref == null ? workspace : NmsInternalNomosRefs.workspace(ref);`,
|
|
3887
3962
|
` if (ws == null || ws.isEmpty) {`,
|
|
3888
3963
|
` throw ArgumentError('${className}.open: pass a workspace: or ref: — the workspace to open and bind');`,
|
|
3889
3964
|
` }`,
|
|
3890
3965
|
` final session = await bridge.acquireSession(ws);`,
|
|
3891
|
-
...(
|
|
3966
|
+
...(authenticationRecovery === undefined
|
|
3892
3967
|
? []
|
|
3893
3968
|
: [
|
|
3894
|
-
`
|
|
3895
|
-
`
|
|
3896
|
-
` if (
|
|
3897
|
-
`
|
|
3969
|
+
` if (recoverAuthentication != null) {`,
|
|
3970
|
+
` final posture = await session.nmsAuthenticationPosture(principal: actor);`,
|
|
3971
|
+
` if (!posture.isReadyFor(actor ?? '')) {`,
|
|
3972
|
+
` await recoverAuthentication();`,
|
|
3973
|
+
` await session.nmsAwaitAuthenticationReady(principal: actor);`,
|
|
3898
3974
|
` }`,
|
|
3899
|
-
` final archive = await rootBundle.load(${dartStr(
|
|
3900
|
-
opts.lawAssetKey
|
|
3901
|
-
)});`,
|
|
3902
|
-
` await bridge.nomosInternalEnsureGeneratedModelInChild(`,
|
|
3903
|
-
` childWorkspace: ws,`,
|
|
3904
|
-
` domain: interface.domain,`,
|
|
3905
|
-
` candidateHash: ${hashConst},`,
|
|
3906
|
-
` lawUsdz: archive.buffer.asUint8List(`,
|
|
3907
|
-
` archive.offsetInBytes, archive.lengthInBytes),`,
|
|
3908
|
-
` );`,
|
|
3909
3975
|
` }`,
|
|
3910
3976
|
]),
|
|
3911
|
-
` return
|
|
3912
|
-
` attempt: () => bind(session, actor: actor${
|
|
3977
|
+
` return bind(session, actor: actor${
|
|
3913
3978
|
manualEncryptionPresent ? ", keyStore: keyStore" : ""
|
|
3914
|
-
})
|
|
3915
|
-
` handle: session.nomosInternalHandleModelChange,`,
|
|
3916
|
-
` );`,
|
|
3979
|
+
});`,
|
|
3917
3980
|
` }`,
|
|
3918
3981
|
``,
|
|
3919
3982
|
` /// The session workspace this client is bound to (from the [NomosSession] handle, when constructed with`,
|
|
@@ -4010,7 +4073,7 @@ export function generateDartBridgeClient(
|
|
|
4010
4073
|
` Future<NomosOfferOutcome> _offer(NmsGeneratedCall call, {List<NomosConflictSnapshot> conflictSnapshots = const [], NomosOfferAuthenticationProof authenticationProof = NomosOfferAuthenticationProof.sessionCredentials}) async {`,
|
|
4011
4074
|
` await _preflightOnce();`,
|
|
4012
4075
|
` await _prepareCommand(call.directive);`,
|
|
4013
|
-
|
|
4076
|
+
` Future<NomosOfferOutcome> attempt() => bridge.offerCallUsdaOutcome(call: call, actor: actor, conflictSnapshots: conflictSnapshots, authenticationProof: authenticationProof);`,
|
|
4014
4077
|
...(authenticationRecovery === undefined
|
|
4015
4078
|
? [` return attempt();`]
|
|
4016
4079
|
: [
|
|
@@ -4020,7 +4083,9 @@ export function generateDartBridgeClient(
|
|
|
4020
4083
|
` } on NomosKernelCallFailure {`,
|
|
4021
4084
|
` final posture = await bridge.nmsAuthenticationPosture(principal: actor);`,
|
|
4022
4085
|
` if (posture.providerToken != NomosProviderTokenVerification.noMatchingKey) rethrow;`,
|
|
4023
|
-
` await ${safeMethod(
|
|
4086
|
+
` await ${safeMethod(
|
|
4087
|
+
authenticationRecovery.id
|
|
4088
|
+
)}(const ${dartActionInputClassName(
|
|
4024
4089
|
authenticationRecovery
|
|
4025
4090
|
)}());`,
|
|
4026
4091
|
` await bridge.nmsAwaitAuthenticationReady(principal: actor);`,
|