@githolon/dsl 0.108.3 → 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 +10 -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 +372 -106
- package/dart/lib/src/runner/offline-install.mjs +171 -11
- 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 +314 -82
- 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 -16
- 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
|
@@ -4390,6 +4390,8 @@ __export(engine_exports, {
|
|
|
4390
4390
|
author: () => author,
|
|
4391
4391
|
authorEphemeral: () => authorEphemeral,
|
|
4392
4392
|
authorKeygen: () => authorKeygen,
|
|
4393
|
+
authorModelLifecyclePromotion: () => authorModelLifecyclePromotion,
|
|
4394
|
+
authorModelLifecycleStage: () => authorModelLifecycleStage,
|
|
4393
4395
|
authorOn: () => authorOn,
|
|
4394
4396
|
authorOperatorCall: () => authorOperatorCall,
|
|
4395
4397
|
backupHead: () => backupHead,
|
|
@@ -4462,6 +4464,7 @@ __export(engine_exports, {
|
|
|
4462
4464
|
lawParity: () => lawParity,
|
|
4463
4465
|
ledgerStats: () => ledgerStats,
|
|
4464
4466
|
listRefusedRefs: () => listRefusedRefs,
|
|
4467
|
+
localCustodySnapshot: () => localCustodySnapshot,
|
|
4465
4468
|
materializeRefused: () => materializeRefused,
|
|
4466
4469
|
memoryDropProbe: () => memoryDropProbe,
|
|
4467
4470
|
migrateHomes: () => migrateHomes,
|
|
@@ -6274,6 +6277,16 @@ function prepareManagedPromotion(report, candidateHash, domain, opts = {}) {
|
|
|
6274
6277
|
error: `the package-wide report does not cover the generated business area '${domain}'`
|
|
6275
6278
|
};
|
|
6276
6279
|
}
|
|
6280
|
+
const dispositions = report.dispositions;
|
|
6281
|
+
const changesStableIdentity = dispositions !== void 0 && (dispositions.retired.length > 0 || Object.keys(dispositions.retyped).length > 0 || Object.keys(dispositions.rebinds).length > 0);
|
|
6282
|
+
if (changesStableIdentity && !acceptanceReason) {
|
|
6283
|
+
return {
|
|
6284
|
+
ok: false,
|
|
6285
|
+
stage: "acceptance",
|
|
6286
|
+
report,
|
|
6287
|
+
error: "the current-frontier transition changes stable business identities and needs an attributed acceptance reason"
|
|
6288
|
+
};
|
|
6289
|
+
}
|
|
6277
6290
|
const preflight = {
|
|
6278
6291
|
candidateHash,
|
|
6279
6292
|
// A package may be reached through any one of its generated domain clients. The
|
|
@@ -10898,6 +10911,7 @@ var AuthoringOptionsSchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_ke
|
|
|
10898
10911
|
var CapturedClockSchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 49);
|
|
10899
10912
|
var BirthCertificateSchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 50);
|
|
10900
10913
|
var SignBirthCertificateRequestSchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 51);
|
|
10914
|
+
var ModelLifecycleIntentSchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 53);
|
|
10901
10915
|
var OfferRequestSchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 54);
|
|
10902
10916
|
var QueryRequestSchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 57);
|
|
10903
10917
|
var QueryAuthoritySchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 64);
|
|
@@ -10912,6 +10926,10 @@ var PendingConflictsQuerySchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_
|
|
|
10912
10926
|
var PendingConflictSummaryQuerySchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 77);
|
|
10913
10927
|
var NamedQuerySchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 78);
|
|
10914
10928
|
var FrontierTransitionRequestSchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 79);
|
|
10929
|
+
var FrontierTransitionDispositionMapEntrySchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 80);
|
|
10930
|
+
var FrontierTransitionDispositionsSchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 81);
|
|
10931
|
+
var FrontierTransitionViolationSchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 82);
|
|
10932
|
+
var FrontierTransitionResponseSchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 83);
|
|
10915
10933
|
var AuthorizationObjectSchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 84);
|
|
10916
10934
|
var AuthorizationSubjectSchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 85);
|
|
10917
10935
|
var AuthorizationConsistencySchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 87);
|
|
@@ -10950,6 +10968,8 @@ var NomosValueSchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel,
|
|
|
10950
10968
|
var NomosStructSchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 166);
|
|
10951
10969
|
var BirthOutcomeStatusSchema = /* @__PURE__ */ enumDesc(file_nomos_kernel_v1_kernel, 0);
|
|
10952
10970
|
var BirthOutcomeStatus = /* @__PURE__ */ tsEnum(BirthOutcomeStatusSchema);
|
|
10971
|
+
var ModelLifecycleOperationSchema = /* @__PURE__ */ enumDesc(file_nomos_kernel_v1_kernel, 3);
|
|
10972
|
+
var ModelLifecycleOperation = /* @__PURE__ */ tsEnum(ModelLifecycleOperationSchema);
|
|
10953
10973
|
var QueryResultFormatSchema = /* @__PURE__ */ enumDesc(file_nomos_kernel_v1_kernel, 5);
|
|
10954
10974
|
var QueryResultFormat = /* @__PURE__ */ tsEnum(QueryResultFormatSchema);
|
|
10955
10975
|
var QueryOperationSchema = /* @__PURE__ */ enumDesc(file_nomos_kernel_v1_kernel, 6);
|
|
@@ -11036,6 +11056,10 @@ function presentOfferAdmitted(admitted) {
|
|
|
11036
11056
|
};
|
|
11037
11057
|
}
|
|
11038
11058
|
|
|
11059
|
+
// src/_adapter/proto/nomos/custody/v1/snapshot_pb.js
|
|
11060
|
+
var file_nomos_custody_v1_snapshot = /* @__PURE__ */ fileDesc("Ch9ub21vcy9jdXN0b2R5L3YxL3NuYXBzaG90LnByb3RvEhBub21vcy5jdXN0b2R5LnYxIqACChRMb2NhbEN1c3RvZHlTbmFwc2hvdBIWCg5mb3JtYXRfdmVyc2lvbhgBIAEoDRIWCgl3b3Jrc3BhY2UYAiABKAlIAIgBARIMCgR0cmVlGAMgASgMEhcKCmNoZWNrcG9pbnQYBCABKAxIAYgBARIVCghmcm9udGllchgFIAEoDEgCiAEBEhMKBmN1cnNvchgGIAEoCUgDiAEBEhcKCnN0YXRlX3Jvb3QYByABKAlIBIgBARIYCgtzeW5jZWRfYmFzZRgIIAEoCUgFiAEBQgwKCl93b3Jrc3BhY2VCDQoLX2NoZWNrcG9pbnRCCwoJX2Zyb250aWVyQgkKB19jdXJzb3JCDQoLX3N0YXRlX3Jvb3RCDgoMX3N5bmNlZF9iYXNlYgZwcm90bzM");
|
|
11061
|
+
var LocalCustodySnapshotSchema = /* @__PURE__ */ messageDesc(file_nomos_custody_v1_snapshot, 0);
|
|
11062
|
+
|
|
11039
11063
|
// src/_adapter/engine.ts
|
|
11040
11064
|
var enc3 = new TextEncoder();
|
|
11041
11065
|
var dec3 = new TextDecoder();
|
|
@@ -11586,6 +11610,100 @@ var closureInstallPayload = (hash, installedBy, options = {}) => ({
|
|
|
11586
11610
|
function authorOperatorCall(eng2, ws, call, controllerHash, opts = {}) {
|
|
11587
11611
|
return author(eng2, ws, call.domain, call.directive, call.canonicalUsda, controllerHash, opts);
|
|
11588
11612
|
}
|
|
11613
|
+
function authorModelLifecycleStage(eng2, ws, lawUsdz, installedBy, supersedes, opts = {}) {
|
|
11614
|
+
const sequence = eng2.seq++;
|
|
11615
|
+
const captured = {
|
|
11616
|
+
physical: Date.now(),
|
|
11617
|
+
logical: 0,
|
|
11618
|
+
replica: eng2.replica
|
|
11619
|
+
};
|
|
11620
|
+
const response = offerRequestResponse(eng2, create(OfferRequestSchema, {
|
|
11621
|
+
target: protoTarget(ws),
|
|
11622
|
+
intent: {
|
|
11623
|
+
case: "modelLifecycle",
|
|
11624
|
+
value: create(ModelLifecycleIntentSchema, {
|
|
11625
|
+
operation: ModelLifecycleOperation.STAGE,
|
|
11626
|
+
installedBy,
|
|
11627
|
+
supersedes
|
|
11628
|
+
})
|
|
11629
|
+
},
|
|
11630
|
+
authoring: create(AuthoringOptionsSchema, {
|
|
11631
|
+
actor: opts.actor ?? "",
|
|
11632
|
+
authToken: opts.authToken ? enc3.encode(opts.authToken) : new Uint8Array(),
|
|
11633
|
+
authorSecret: opts.authorSecret ? bytesFromB64(opts.authorSecret) : new Uint8Array(),
|
|
11634
|
+
validFromEpoch: opts.validFromEpoch === void 0 ? void 0 : BigInt(opts.validFromEpoch),
|
|
11635
|
+
rng: osEntropyBuffer(opts.rngDraws ?? 64),
|
|
11636
|
+
deferProjection: opts.deferProjection !== false,
|
|
11637
|
+
spans: opts.spans ?? false,
|
|
11638
|
+
sequence: BigInt(sequence),
|
|
11639
|
+
clock: create(CapturedClockSchema, {
|
|
11640
|
+
physicalMillis: BigInt(captured.physical),
|
|
11641
|
+
logical: captured.logical,
|
|
11642
|
+
replica: BigInt(captured.replica)
|
|
11643
|
+
})
|
|
11644
|
+
}),
|
|
11645
|
+
lawUsdz
|
|
11646
|
+
}));
|
|
11647
|
+
return offerResult(response);
|
|
11648
|
+
}
|
|
11649
|
+
function authorModelLifecyclePromotion(eng2, ws, transition, replaces, acceptanceReason, opts = {}) {
|
|
11650
|
+
const sequence = eng2.seq++;
|
|
11651
|
+
const captured = {
|
|
11652
|
+
physical: Date.now(),
|
|
11653
|
+
logical: 0,
|
|
11654
|
+
replica: eng2.replica
|
|
11655
|
+
};
|
|
11656
|
+
const dispositions = transition.dispositions === void 0 ? void 0 : create(FrontierTransitionDispositionsSchema, {
|
|
11657
|
+
retired: [...transition.dispositions.retired],
|
|
11658
|
+
retyped: Object.entries(transition.dispositions.retyped).map(
|
|
11659
|
+
([sourceStableId, acknowledgement]) => create(FrontierTransitionDispositionMapEntrySchema, { sourceStableId, acknowledgement })
|
|
11660
|
+
),
|
|
11661
|
+
rebinds: Object.entries(transition.dispositions.rebinds).map(
|
|
11662
|
+
([sourceStableId, acknowledgement]) => create(FrontierTransitionDispositionMapEntrySchema, { sourceStableId, acknowledgement })
|
|
11663
|
+
),
|
|
11664
|
+
acknowledgedBy: transition.dispositions.ackBy
|
|
11665
|
+
});
|
|
11666
|
+
const response = offerRequestResponse(eng2, create(OfferRequestSchema, {
|
|
11667
|
+
target: protoTarget(ws),
|
|
11668
|
+
intent: {
|
|
11669
|
+
case: "modelLifecycle",
|
|
11670
|
+
value: create(ModelLifecycleIntentSchema, {
|
|
11671
|
+
operation: ModelLifecycleOperation.PROMOTE,
|
|
11672
|
+
replaces,
|
|
11673
|
+
acceptanceReason,
|
|
11674
|
+
transition: create(FrontierTransitionResponseSchema, {
|
|
11675
|
+
atHash: transition.atHash,
|
|
11676
|
+
currentHash: transition.currentHash,
|
|
11677
|
+
atStateRoot: transition.atStateRoot ?? "",
|
|
11678
|
+
candidateHash: transition.candidateHash,
|
|
11679
|
+
domain: transition.domain,
|
|
11680
|
+
domains: [...transition.domains ?? []],
|
|
11681
|
+
ready: transition.ready,
|
|
11682
|
+
violations: transition.evolve.violations.map(
|
|
11683
|
+
({ domain, detail }) => create(FrontierTransitionViolationSchema, { domain, detail })
|
|
11684
|
+
),
|
|
11685
|
+
dispositions
|
|
11686
|
+
})
|
|
11687
|
+
})
|
|
11688
|
+
},
|
|
11689
|
+
authoring: create(AuthoringOptionsSchema, {
|
|
11690
|
+
actor: opts.actor ?? "",
|
|
11691
|
+
authToken: opts.authToken ? enc3.encode(opts.authToken) : new Uint8Array(),
|
|
11692
|
+
authorSecret: opts.authorSecret ? bytesFromB64(opts.authorSecret) : new Uint8Array(),
|
|
11693
|
+
validFromEpoch: opts.validFromEpoch === void 0 ? void 0 : BigInt(opts.validFromEpoch),
|
|
11694
|
+
rng: osEntropyBuffer(opts.rngDraws ?? 64),
|
|
11695
|
+
deferProjection: opts.deferProjection !== false,
|
|
11696
|
+
spans: opts.spans ?? false,
|
|
11697
|
+
sequence: BigInt(sequence),
|
|
11698
|
+
clock: create(CapturedClockSchema, {
|
|
11699
|
+
physicalMillis: BigInt(captured.physical),
|
|
11700
|
+
logical: captured.logical,
|
|
11701
|
+
replica: BigInt(captured.replica)
|
|
11702
|
+
})
|
|
11703
|
+
})
|
|
11704
|
+
}));
|
|
11705
|
+
return offerResult(response);
|
|
11706
|
+
}
|
|
11589
11707
|
function initializeWorkspace(eng2, ws, installedBy = eng2.installedBy) {
|
|
11590
11708
|
const sequence = eng2.seq++;
|
|
11591
11709
|
const captured = {
|
|
@@ -12704,8 +12822,11 @@ async function reconcileRequiredCustodyHead(eng2, ws, ledger, requiredCustodyHea
|
|
|
12704
12822
|
if (!/^[0-9a-f]{40}$/.test(requiredCustodyHead)) {
|
|
12705
12823
|
throw new Error("required custody head must be a lowercase 40-hex git object id");
|
|
12706
12824
|
}
|
|
12707
|
-
const
|
|
12708
|
-
|
|
12825
|
+
const resident = eng2.mounted.get(ws);
|
|
12826
|
+
const current = resident ? kgit.resolveRef(eng2, gitdirOf(ws), BRANCH) : "";
|
|
12827
|
+
if (current === requiredCustodyHead && resident?.remoteMain === requiredCustodyHead && !resident.restoreError) {
|
|
12828
|
+
return resident;
|
|
12829
|
+
}
|
|
12709
12830
|
if (current && isDirty(eng2, ws)) {
|
|
12710
12831
|
throw new Error(
|
|
12711
12832
|
`workspace '${ws}' has verified but unsealed resident work; refusing to discard it while reconciling custody`
|
|
@@ -12718,6 +12839,12 @@ async function reconcileRequiredCustodyHead(eng2, ws, ledger, requiredCustodyHea
|
|
|
12718
12839
|
`workspace '${ws}' could not reconcile required custody ${requiredCustodyHead}: ${mounted.restoreError || "custody has no verified main"}`
|
|
12719
12840
|
);
|
|
12720
12841
|
}
|
|
12842
|
+
const reconciled = kgit.resolveRef(eng2, gitdirOf(ws), BRANCH);
|
|
12843
|
+
if (reconciled !== requiredCustodyHead || mounted.remoteMain !== requiredCustodyHead) {
|
|
12844
|
+
throw new Error(
|
|
12845
|
+
`workspace '${ws}' restored custody ${mounted.remoteMain || reconciled || "none"}, not required custody ${requiredCustodyHead}`
|
|
12846
|
+
);
|
|
12847
|
+
}
|
|
12721
12848
|
return mounted;
|
|
12722
12849
|
}
|
|
12723
12850
|
function assertAuthored(res, what) {
|
|
@@ -12855,7 +12982,10 @@ async function applyWorkspaceEffect(eng2, ws, ledger, requestBytes, admissionChe
|
|
|
12855
12982
|
const result = response.outcome.value.result;
|
|
12856
12983
|
if (!result || result.outcome.case !== "admitted") {
|
|
12857
12984
|
const outcome = result?.outcome.case || "empty";
|
|
12858
|
-
|
|
12985
|
+
const detail = result?.outcome.case === "refused" ? result.outcome.value.refusal?.message : void 0;
|
|
12986
|
+
throw new Error(
|
|
12987
|
+
`target kernel ${outcome} the workspace effect${detail ? `: ${detail}` : ""}`
|
|
12988
|
+
);
|
|
12859
12989
|
}
|
|
12860
12990
|
await custodyPush(eng2, ws, ledger, { remoteRef: "refs/heads/main", ffOnly: true });
|
|
12861
12991
|
return toBinary(ApplyWorkspaceEffectResponseSchema, response.outcome.value);
|
|
@@ -12966,8 +13096,8 @@ function prepareRemoteOffer(eng2, ws, requestBytes) {
|
|
|
12966
13096
|
if (authoring.authorSecret.byteLength !== 0) {
|
|
12967
13097
|
throw new Error("remote offer preparation refuses client signing secrets");
|
|
12968
13098
|
}
|
|
12969
|
-
if (request.intent.case !== "call") {
|
|
12970
|
-
throw new Error("remote offer preparation requires a typed directive call");
|
|
13099
|
+
if (request.intent.case !== "call" && request.intent.case !== "modelLifecycle") {
|
|
13100
|
+
throw new Error("remote offer preparation requires a typed directive or model-lifecycle call");
|
|
12971
13101
|
}
|
|
12972
13102
|
const response = callProto(eng2.ex, {
|
|
12973
13103
|
case: "prepareRemoteOffer",
|
|
@@ -13774,7 +13904,13 @@ function packDelta(eng2, ws, base, head) {
|
|
|
13774
13904
|
case: "packDelta",
|
|
13775
13905
|
value: create(PackDeltaRequestSchema, { repo: repoArgOf(ws), base: base || void 0, head })
|
|
13776
13906
|
}, "packDelta").pack;
|
|
13777
|
-
} catch {
|
|
13907
|
+
} catch (error) {
|
|
13908
|
+
console.error("[nomos-custody-pack] packDelta refused", {
|
|
13909
|
+
workspace: ws,
|
|
13910
|
+
base: base || "<full>",
|
|
13911
|
+
head,
|
|
13912
|
+
error: String(error)
|
|
13913
|
+
});
|
|
13778
13914
|
return null;
|
|
13779
13915
|
}
|
|
13780
13916
|
}
|
|
@@ -14457,11 +14593,12 @@ function promotePreflighted(eng2, ws, domain, candidateHash, opts = {}) {
|
|
|
14457
14593
|
acceptanceReason: _acceptance,
|
|
14458
14594
|
...authorOpts
|
|
14459
14595
|
} = opts;
|
|
14460
|
-
const result =
|
|
14596
|
+
const result = authorModelLifecyclePromotion(
|
|
14461
14597
|
eng2,
|
|
14462
14598
|
ws,
|
|
14463
|
-
|
|
14464
|
-
|
|
14599
|
+
report,
|
|
14600
|
+
prepared.payload.replaces,
|
|
14601
|
+
prepared.payload.preflight.acceptanceReason,
|
|
14465
14602
|
{
|
|
14466
14603
|
...authorOpts,
|
|
14467
14604
|
...actingPrincipal ? { actor: actingPrincipal } : {}
|
|
@@ -14952,7 +15089,10 @@ async function emitOutbox(eng2, ws, ledger, workspaceEffectBase = "", admissionC
|
|
|
14952
15089
|
const targetResult = response.result;
|
|
14953
15090
|
if (!targetResult || targetResult.outcome.case !== "admitted") {
|
|
14954
15091
|
const outcome = targetResult?.outcome.case || "empty";
|
|
14955
|
-
|
|
15092
|
+
const detail = targetResult?.outcome.case === "refused" ? targetResult.outcome.value.refusal?.message : void 0;
|
|
15093
|
+
throw new Error(
|
|
15094
|
+
`target kernel ${outcome} the workspace effect${detail ? `: ${detail}` : ""}`
|
|
15095
|
+
);
|
|
14956
15096
|
}
|
|
14957
15097
|
break;
|
|
14958
15098
|
}
|
|
@@ -14984,7 +15124,7 @@ async function emitOutbox(eng2, ws, ledger, workspaceEffectBase = "", admissionC
|
|
|
14984
15124
|
} catch (e) {
|
|
14985
15125
|
errors++;
|
|
14986
15126
|
const destination = offer.route.case === "httpsRoute" ? offer.route.value.url : offer.route.case === "workspaceRoute" ? offer.route.value.workspace : offer.destination;
|
|
14987
|
-
log("outbox-deliver-failed", { ws, offerId: offer.id, destination, error: String(e).slice(0,
|
|
15127
|
+
log("outbox-deliver-failed", { ws, offerId: offer.id, destination, error: String(e).slice(0, 2e3) });
|
|
14988
15128
|
}
|
|
14989
15129
|
}
|
|
14990
15130
|
if (delivered.length || errors) log("outbox", { ws, delivered: delivered.length, errors });
|
|
@@ -15017,6 +15157,26 @@ function snapshot(eng2, ws) {
|
|
|
15017
15157
|
const wsDir = dirOf(eng2.preopen.dir.contents.get("ws")).contents.get(ws);
|
|
15018
15158
|
return serializeTree(wsDir);
|
|
15019
15159
|
}
|
|
15160
|
+
function localCustodySnapshot(eng2, ws) {
|
|
15161
|
+
const checkpoint2 = exportCheckpoint(eng2, ws);
|
|
15162
|
+
const tree = snapshot(eng2, ws);
|
|
15163
|
+
const head = headOf(eng2, ws);
|
|
15164
|
+
const body = toBinary(LocalCustodySnapshotSchema, create(LocalCustodySnapshotSchema, {
|
|
15165
|
+
formatVersion: 3,
|
|
15166
|
+
workspace: ws,
|
|
15167
|
+
tree,
|
|
15168
|
+
checkpoint: checkpoint2.blob ? bytesFromB64(checkpoint2.blob) : void 0,
|
|
15169
|
+
frontier: checkpoint2.frontier ? bytesFromB64(checkpoint2.frontier) : void 0,
|
|
15170
|
+
cursor: checkpoint2.cursor === void 0 ? void 0 : String(checkpoint2.cursor),
|
|
15171
|
+
stateRoot: checkpoint2.stateRoot,
|
|
15172
|
+
syncedBase: head || void 0
|
|
15173
|
+
}));
|
|
15174
|
+
const prefix = enc3.encode("NOMOS-CUSTODY-PB3\n");
|
|
15175
|
+
const framed = new Uint8Array(prefix.byteLength + body.byteLength);
|
|
15176
|
+
framed.set(prefix);
|
|
15177
|
+
framed.set(body, prefix.byteLength);
|
|
15178
|
+
return framed;
|
|
15179
|
+
}
|
|
15020
15180
|
function stats(eng2) {
|
|
15021
15181
|
return {
|
|
15022
15182
|
mounted: eng2.mounted.size,
|
|
@@ -284,6 +284,10 @@ Future<T> nmsInternalConvergeGeneratedModel<T>({
|
|
|
284
284
|
try {
|
|
285
285
|
return await attempt();
|
|
286
286
|
} on DomainClientModelAdoptionRequired catch (change) {
|
|
287
|
+
// A lifecycle response with no lawful next action is a terminal refusal,
|
|
288
|
+
// not a convergence step. Preserve the kernel's exact diagnostic instead
|
|
289
|
+
// of retrying until a generic stall obscures it.
|
|
290
|
+
if (!change.canStage && !change.canAccept) throw change;
|
|
287
291
|
final step = '${change.expectedDomainKey}\u0000'
|
|
288
292
|
'${change.expectedDomainHash}\u0000'
|
|
289
293
|
'${change.status}\u0000'
|
|
@@ -300,10 +304,11 @@ Future<T> nmsInternalConvergeGeneratedModel<T>({
|
|
|
300
304
|
}
|
|
301
305
|
try {
|
|
302
306
|
await handle(change);
|
|
303
|
-
} on DomainClientModelAdoptionRequired {
|
|
307
|
+
} on DomainClientModelAdoptionRequired catch (nextChange) {
|
|
304
308
|
// A transient preflight/acceptance race re-enters the same typed step.
|
|
305
309
|
// The per-step budget above prevents a genuine loop without limiting
|
|
306
310
|
// how many distinct historical generations may converge.
|
|
311
|
+
if (!nextChange.canStage && !nextChange.canAccept) throw nextChange;
|
|
307
312
|
}
|
|
308
313
|
}
|
|
309
314
|
}
|
|
@@ -379,35 +384,56 @@ class DomainNotInstalledForSession implements Exception {
|
|
|
379
384
|
/// cause classified; returns normally when the session serves the client's law compatibly.
|
|
380
385
|
extension NomosSessionCompat on NomosSession {
|
|
381
386
|
Future<void> assertCompatible(NomosClientInterface iface) async {
|
|
382
|
-
//
|
|
387
|
+
// The exact generated package identity is stronger than a structural comparison. The kernel's
|
|
388
|
+
// point-readable `law/current.usda` answers this bounded happy path without materialising the
|
|
389
|
+
// complete interface offer. Structural discovery is reserved for genuine version skew.
|
|
383
390
|
List<String> activeKeys = const [];
|
|
391
|
+
List<String> activeHashes = const [];
|
|
384
392
|
try {
|
|
385
|
-
final
|
|
386
|
-
|
|
393
|
+
final law = await lawStatus(iface.expectedDomainHash);
|
|
394
|
+
activeHashes = ((law['current'] as List?) ?? const [])
|
|
395
|
+
.map((value) => value.toString())
|
|
396
|
+
.toList(growable: false);
|
|
397
|
+
if (activeHashes.contains(iface.expectedDomainHash)) return;
|
|
398
|
+
activeKeys = ((law['currentDomains'] as List?) ?? const [])
|
|
399
|
+
.map((value) => value.toString())
|
|
400
|
+
.toList(growable: false);
|
|
401
|
+
// Compatibility is member-scoped, not one enormous application-wide ceremony. Once the
|
|
402
|
+
// authenticated current-law index proves this domain is served, generated reads decode their
|
|
403
|
+
// exact USDA result types and generated offers are judged by the current kernel route. A new
|
|
404
|
+
// member therefore fails closed when used without blocking every older compatible feature.
|
|
405
|
+
if (activeKeys.contains(iface.domain)) return;
|
|
406
|
+
} catch (_) {
|
|
407
|
+
// Preserve the structural fallback for older kernels which do not expose the typed current
|
|
408
|
+
// frontier. Its error classification below remains the authoritative diagnostic path.
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// 1. THE SHAPE AXIS (#72) — the one compatibility notion. The kernel compares the
|
|
412
|
+
// compact generated requirement directly with its authenticated current-law index. Do not
|
|
413
|
+
// export/materialise the complete interface merely to prove the successful path.
|
|
414
|
+
final requirement = iface.requirement;
|
|
415
|
+
if (requirement == null) return;
|
|
416
|
+
Map<String, Object?> verdict;
|
|
417
|
+
try {
|
|
418
|
+
final raw = await checkCompatibility(requirement);
|
|
419
|
+
verdict = (raw['verdict'] is Map)
|
|
420
|
+
? (raw['verdict'] as Map).cast<String, Object?>()
|
|
421
|
+
: raw;
|
|
387
422
|
} catch (e) {
|
|
388
|
-
// co2 blocker #2: a PROJECTION READ FAILURE (an unmountable/failing query shape — the runner surfaces
|
|
389
|
-
// `nomos.projection.read_failed` carrying the failed query) is NOT "not reachable" and MUST NOT be
|
|
390
|
-
// reclassified as trulyNotDeployed downstream — surface it as its own cause with the real fault text.
|
|
391
|
-
final isProjectionFault =
|
|
392
|
-
e.toString().contains('nomos.projection.read_failed') ||
|
|
393
|
-
e.toString().contains('projection read failed');
|
|
394
423
|
throw DomainClientSessionMismatch(
|
|
395
424
|
expectedDomainKey: iface.domain,
|
|
396
425
|
expectedDomainHash: iface.expectedDomainHash,
|
|
397
426
|
expectedInterfaceHash: iface.expectedInterfaceHash,
|
|
398
427
|
sessionId: workspace,
|
|
399
428
|
sessionWorkspace: workspace,
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
hint: isProjectionFault
|
|
404
|
-
? 'the workspace law could NOT be READ — a projection/mount failure (NOT undeployed law): $e'
|
|
405
|
-
: 'the session workspace is not reachable/mounted on this plane: $e',
|
|
429
|
+
activeDomainKeys: activeKeys,
|
|
430
|
+
activeDomainHashes: activeHashes,
|
|
431
|
+
hint: 'the shape-compatibility check itself failed: $e',
|
|
406
432
|
);
|
|
407
433
|
}
|
|
434
|
+
if (verdict['ok'] == true) return;
|
|
408
435
|
|
|
409
|
-
// 2. HASH
|
|
410
|
-
List<String> activeHashes = const [];
|
|
436
|
+
// 2. HASH DIAGNOSIS — only a failed verdict needs retained installation inventory.
|
|
411
437
|
if (iface.compatJson != null && iface.compatJson!.isNotEmpty) {
|
|
412
438
|
try {
|
|
413
439
|
final c = await checkCompat(
|
|
@@ -415,32 +441,34 @@ extension NomosSessionCompat on NomosSession {
|
|
|
415
441
|
iface.requirement);
|
|
416
442
|
if (c != null) activeHashes = c.workspaceRuns;
|
|
417
443
|
} catch (_) {
|
|
418
|
-
/* diagnosis only — never
|
|
444
|
+
/* diagnosis only — never replace the shape verdict */
|
|
419
445
|
}
|
|
420
446
|
}
|
|
421
447
|
|
|
422
|
-
// 3.
|
|
423
|
-
|
|
424
|
-
if (requirement == null) return;
|
|
425
|
-
Map<String, Object?> verdict;
|
|
448
|
+
// 3. DIAGNOSTIC OFFER — only a failed verdict needs the complete active-domain names to
|
|
449
|
+
// distinguish a newer client from the wrong package or a genuinely absent deployment.
|
|
426
450
|
try {
|
|
427
|
-
final
|
|
428
|
-
|
|
429
|
-
? (raw['verdict'] as Map).cast<String, Object?>()
|
|
430
|
-
: raw;
|
|
451
|
+
final offer = await interfaceOffer();
|
|
452
|
+
if (offer != null) activeKeys = offer.keys.toList()..sort();
|
|
431
453
|
} catch (e) {
|
|
454
|
+
final isProjectionFault =
|
|
455
|
+
e.toString().contains('nomos.projection.read_failed') ||
|
|
456
|
+
e.toString().contains('projection read failed');
|
|
432
457
|
throw DomainClientSessionMismatch(
|
|
433
458
|
expectedDomainKey: iface.domain,
|
|
434
459
|
expectedDomainHash: iface.expectedDomainHash,
|
|
435
460
|
expectedInterfaceHash: iface.expectedInterfaceHash,
|
|
436
461
|
sessionId: workspace,
|
|
437
462
|
sessionWorkspace: workspace,
|
|
438
|
-
activeDomainKeys: activeKeys,
|
|
439
463
|
activeDomainHashes: activeHashes,
|
|
440
|
-
|
|
464
|
+
cause: isProjectionFault
|
|
465
|
+
? SessionMismatchCause.projectionReadFailed
|
|
466
|
+
: null,
|
|
467
|
+
hint: isProjectionFault
|
|
468
|
+
? 'the workspace law could NOT be READ — a projection/mount failure (NOT undeployed law): $e'
|
|
469
|
+
: 'the session workspace is not reachable/mounted on this plane: $e',
|
|
441
470
|
);
|
|
442
471
|
}
|
|
443
|
-
if (verdict['ok'] == true) return;
|
|
444
472
|
|
|
445
473
|
final missing =
|
|
446
474
|
((verdict['missing'] as List?) ?? const []).map((e) => '$e').toList();
|
|
@@ -43,7 +43,7 @@ import 'dart:io';
|
|
|
43
43
|
import 'package:crypto/crypto.dart' show sha256;
|
|
44
44
|
import 'package:fixnum/fixnum.dart' show Int64;
|
|
45
45
|
import 'package:nomos_types/nomos_kernel_protocol.dart'
|
|
46
|
-
show BirthCertificate,
|
|
46
|
+
show BirthCertificate, SyncWorkspaceResponse;
|
|
47
47
|
import 'package:nomos_types/nomos_types.dart' show Sha256Digest;
|
|
48
48
|
import 'package:package_config/package_config.dart' show loadPackageConfigUri;
|
|
49
49
|
|
|
@@ -494,6 +494,14 @@ class NomosTestHarness {
|
|
|
494
494
|
/// `sha256(base64decode(...))` is the attestor keyHash a reader enrols to trust this source's signed reads.
|
|
495
495
|
String? devicePublicKey(String workspace) => _devicePublics[workspace];
|
|
496
496
|
|
|
497
|
+
/// The enrolled device identity used by provider nonce binding. Tests which
|
|
498
|
+
/// rotate an external IdP can mint the next proof without handling key bytes.
|
|
499
|
+
String? deviceKeyHash(String workspace) {
|
|
500
|
+
final publicKey = _devicePublics[workspace];
|
|
501
|
+
if (publicKey == null || publicKey.isEmpty) return null;
|
|
502
|
+
return sha256.convert(base64Decode(publicKey)).toString();
|
|
503
|
+
}
|
|
504
|
+
|
|
497
505
|
/// The base64 device SECRET a session was opened with (TEST HARNESS ONLY — the harness IS the device;
|
|
498
506
|
/// a real app keeps this in secure storage). Sign writes to locally-born children with it, e.g.
|
|
499
507
|
/// `bridge.acquireSession(childWs, authorSecret: harness.deviceSecret(home))`.
|
|
@@ -1480,6 +1488,7 @@ class NomosTestHarness {
|
|
|
1480
1488
|
static Future<NomosTestHarness> cloudBound({
|
|
1481
1489
|
required NomosTestCloud cloud,
|
|
1482
1490
|
required List<String> lawUsdzPaths,
|
|
1491
|
+
NmsApplicationFrameworkCalls? applicationFrameworkCalls,
|
|
1483
1492
|
String platformWorkspace = 'test-platform',
|
|
1484
1493
|
required String subject,
|
|
1485
1494
|
String? authToken,
|
|
@@ -1500,6 +1509,13 @@ class NomosTestHarness {
|
|
|
1500
1509
|
'no lawUsdzPaths — pass the build/<name>.law.usdz `npx githolon compile` emits',
|
|
1501
1510
|
);
|
|
1502
1511
|
}
|
|
1512
|
+
final frameworkCalls = applicationFrameworkCalls;
|
|
1513
|
+
if (frameworkCalls == null) {
|
|
1514
|
+
throw NomosTestHarnessException(
|
|
1515
|
+
'cloudBound requires this application\'s compiler-generated '
|
|
1516
|
+
'NmsApplicationFrameworkCalls; raw framework directive transport is not supported',
|
|
1517
|
+
);
|
|
1518
|
+
}
|
|
1503
1519
|
final entry = nodeEntry ??
|
|
1504
1520
|
await _resolveEntry(node, resolveFrom ?? Directory.current.path);
|
|
1505
1521
|
final bareUid =
|
|
@@ -1853,56 +1869,18 @@ class NomosTestHarness {
|
|
|
1853
1869
|
'capturedClock': DateTime.now().toUtc().millisecondsSinceEpoch,
|
|
1854
1870
|
'epoch': 0,
|
|
1855
1871
|
}, secret);
|
|
1856
|
-
// Mount the target workspace in the SAME kernel plane and author through its own
|
|
1857
|
-
// installed law. The HTTP host is neither given raw business fields nor a signing key;
|
|
1858
|
-
// sync carries the already-sealed offer and the receiving kernel replays the one gate.
|
|
1859
|
-
// The enroll route was still resolved above to prove the generated public surface
|
|
1860
|
-
// names this semantic route exactly once, but route strings grant no authority here.
|
|
1861
|
-
final platformAuthorSession = '$clientId-platform-author';
|
|
1862
|
-
await plane.connect(
|
|
1863
|
-
cloud: cloud.origin,
|
|
1864
|
-
workspace: platformWorkspace,
|
|
1865
|
-
session: platformAuthorSession,
|
|
1866
|
-
clientId: '$clientId-platform-author',
|
|
1867
|
-
authToken: idpToken,
|
|
1868
|
-
authorSecret: secret,
|
|
1869
|
-
);
|
|
1870
|
-
await plane.offerDirective(
|
|
1871
|
-
domain: applicationHomeDomain,
|
|
1872
|
-
directiveId: 'enrollFirstDevice',
|
|
1873
|
-
domainHash: controlHomeLawHash,
|
|
1874
|
-
actor: subject,
|
|
1875
|
-
authorSecret: secret,
|
|
1876
|
-
session: platformAuthorSession,
|
|
1877
|
-
payload: {
|
|
1878
|
-
'keyHash': keyHash,
|
|
1879
|
-
'verifiedVia': issuer,
|
|
1880
|
-
'validFromEpoch': 0,
|
|
1881
|
-
'grantedAt': DateTime.now().toUtc().toIso8601String(),
|
|
1882
|
-
'idpToken': idpToken,
|
|
1883
|
-
// The framework law owns this USDA object shape. Keep the
|
|
1884
|
-
// compiler-generated protobuf certificate structured while
|
|
1885
|
-
// lowering it into that domain value; never resurrect the
|
|
1886
|
-
// retired JSON string envelope (`delegationCert`).
|
|
1887
|
-
'delegationCertificate':
|
|
1888
|
-
_birthCertificateLawValue(delegationCert),
|
|
1889
|
-
},
|
|
1890
|
-
);
|
|
1891
|
-
await plane.sync(
|
|
1892
|
-
session: platformAuthorSession,
|
|
1893
|
-
);
|
|
1894
1872
|
return (
|
|
1895
1873
|
publicB64: publicB64,
|
|
1896
1874
|
secret: secret,
|
|
1897
1875
|
keyHash: keyHash,
|
|
1898
1876
|
idpToken: idpToken,
|
|
1877
|
+
issuer: issuer,
|
|
1878
|
+
delegationCert: delegationCert,
|
|
1899
1879
|
);
|
|
1900
1880
|
},
|
|
1901
1881
|
);
|
|
1902
|
-
// 5/6 — THE HOME CEREMONY.
|
|
1903
|
-
//
|
|
1904
|
-
// business fields or a signing secret and has no birth-authoring lane. The offer carries the
|
|
1905
|
-
// typed device-signed certificate. The kernel derives and enrolls the founding signer from it.
|
|
1882
|
+
// 5/6 — THE PRODUCTION HOME CEREMONY. Platform remains remote: Runtime v2 prepares both generated
|
|
1883
|
+
// calls against its live frontier, signs with this device, and opens the resulting Home custody.
|
|
1906
1884
|
String? home;
|
|
1907
1885
|
await _phased(
|
|
1908
1886
|
phase: '5/6: remote home-birth ceremony (attested)',
|
|
@@ -1912,65 +1890,49 @@ class NomosTestHarness {
|
|
|
1912
1890
|
onDiagnostic: onDiagnostic,
|
|
1913
1891
|
run: () async {
|
|
1914
1892
|
final homeName = _receivingGroupNameOf(subject);
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
);
|
|
1952
|
-
}
|
|
1953
|
-
final birthSync = await plane.sync(
|
|
1954
|
-
session: '$clientId-platform-author',
|
|
1955
|
-
);
|
|
1956
|
-
if (birthSync.pushFailed ||
|
|
1957
|
-
birthSync.diverged ||
|
|
1958
|
-
birthSync.stillAhead) {
|
|
1893
|
+
final bornAt = DateTime.now().toUtc().toIso8601String();
|
|
1894
|
+
final result = await plane
|
|
1895
|
+
.session('$clientId-onboard-scratch')
|
|
1896
|
+
.nomosInternalBirthReceivingGroup(
|
|
1897
|
+
parent: platformWorkspace,
|
|
1898
|
+
principal: bareUid,
|
|
1899
|
+
subject: subject,
|
|
1900
|
+
device: {
|
|
1901
|
+
'public': device.publicB64,
|
|
1902
|
+
'secret': device.secret,
|
|
1903
|
+
},
|
|
1904
|
+
authToken: device.idpToken,
|
|
1905
|
+
idToken: device.idpToken,
|
|
1906
|
+
verifiedVia: device.issuer,
|
|
1907
|
+
bornAt: bornAt,
|
|
1908
|
+
enrollFirstDeviceCall: frameworkCalls.enrollFirstDevice(
|
|
1909
|
+
domain: applicationHomeDomain,
|
|
1910
|
+
workspace: platformWorkspace,
|
|
1911
|
+
keyHash: device.keyHash,
|
|
1912
|
+
verifiedVia: device.issuer,
|
|
1913
|
+
validFromEpoch: 0,
|
|
1914
|
+
grantedAt: bornAt,
|
|
1915
|
+
idpToken: device.idpToken,
|
|
1916
|
+
delegationCertificate: device.delegationCert,
|
|
1917
|
+
),
|
|
1918
|
+
birthReceivingGroupCall: frameworkCalls.birthReceivingGroup(
|
|
1919
|
+
domain: applicationHomeDomain,
|
|
1920
|
+
workspace: platformWorkspace,
|
|
1921
|
+
owner: subject,
|
|
1922
|
+
frameworkHash: Sha256Digest(frameworkHash!),
|
|
1923
|
+
lawHash: Sha256Digest(applicationHomeLawHash),
|
|
1924
|
+
bornAt: bornAt,
|
|
1925
|
+
),
|
|
1926
|
+
confirm: true,
|
|
1927
|
+
);
|
|
1928
|
+
if (result.home != homeName) {
|
|
1959
1929
|
throw NomosTestHarnessException(
|
|
1960
|
-
'cloudBound:
|
|
1930
|
+
'cloudBound: production custody ceremony returned ${result.home} instead of "$homeName"',
|
|
1961
1931
|
);
|
|
1962
1932
|
}
|
|
1963
|
-
|
|
1964
|
-
final failedRemoteBirth = remoteOutcomes.where(
|
|
1965
|
-
(entry) =>
|
|
1966
|
-
entry.hasWorkspace() &&
|
|
1967
|
-
entry.workspace == homeName &&
|
|
1968
|
-
entry.status != BirthOutcomeStatus.BIRTH_OUTCOME_STATUS_BORN,
|
|
1969
|
-
);
|
|
1970
|
-
if (failedRemoteBirth.isNotEmpty) {
|
|
1933
|
+
if (!result.verified) {
|
|
1971
1934
|
throw NomosTestHarnessException(
|
|
1972
|
-
'cloudBound:
|
|
1973
|
-
'${failedRemoteBirth.toList()}',
|
|
1935
|
+
'cloudBound: born Home failed kernel verification: ${result.verificationError}',
|
|
1974
1936
|
);
|
|
1975
1937
|
}
|
|
1976
1938
|
home = homeName;
|